memscope_rs/analysis/
closure_analysis.rs

1//! Closure capture analysis for memscope
2//!
3//! This module provides detailed analysis of closure captures, including:
4//! - Variable capture modes (by value, by reference, by mutable reference)
5//! - Lifetime tracking of captured variables
6//! - Memory impact analysis of closures
7//! - Optimization suggestions for closure usage
8
9use crate::core::types::AllocationInfo;
10use serde::{Deserialize, Serialize};
11use std::collections::HashMap;
12use std::sync::{Arc, Mutex, OnceLock};
13
14/// Global closure analyzer instance
15static GLOBAL_CLOSURE_ANALYZER: OnceLock<Arc<ClosureAnalyzer>> = OnceLock::new();
16
17/// Get the global closure analyzer instance
18pub fn get_global_closure_analyzer() -> Arc<ClosureAnalyzer> {
19    GLOBAL_CLOSURE_ANALYZER
20        .get_or_init(|| Arc::new(ClosureAnalyzer::new()))
21        .clone()
22}
23
24/// Advanced closure capture analysis
25pub struct ClosureAnalyzer {
26    /// Tracked closures and their captures
27    closures: Mutex<HashMap<usize, ClosureInfo>>,
28    /// Capture events history
29    capture_events: Mutex<Vec<CaptureEvent>>,
30    /// Lifetime relationships between closures and captured variables
31    lifetime_graph: Mutex<LifetimeGraph>,
32}
33
34impl ClosureAnalyzer {
35    /// Create a new closure analyzer
36    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    /// Register a new closure with its captures
45    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        // Record capture events
57        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        // Update lifetime graph
71        if let Ok(mut graph) = self.lifetime_graph.lock() {
72            graph.add_closure_relationships(closure_ptr, &captures);
73        }
74
75        // Store closure info
76        if let Ok(mut closures) = self.closures.lock() {
77            closures.insert(closure_ptr, closure_info);
78        }
79    }
80
81    /// Track when a closure is dropped
82    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                // Record drop events for all captures
86                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        // Update lifetime graph
103        if let Ok(mut graph) = self.lifetime_graph.lock() {
104            graph.remove_closure(closure_ptr);
105        }
106    }
107
108    /// Analyze closure capture patterns in allocations
109    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        // Calculate statistics
127        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    /// Check if a type represents a closure
144    fn is_closure_type(&self, type_name: &str) -> bool {
145        // Rust closures typically have names like:
146        // - "closure" (in debug builds)
147        // - Complex mangled names containing "closure"
148        // - Function pointer types
149        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    /// Analyze a specific closure allocation
157    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    /// Estimate number of captures based on closure size
176    fn estimate_captures_from_size(&self, size: usize) -> usize {
177        // Rough estimation: each capture is typically 8-24 bytes
178        // depending on the type (pointer, value, etc.)
179        if size <= 8 {
180            0 // Empty closure or single small capture
181        } else if size <= 32 {
182            2 // Small number of captures
183        } else if size <= 128 {
184            8 // Medium number of captures
185        } else {
186            size / 16 // Large number of captures (rough estimate)
187        }
188    }
189
190    /// Classify the type of closure
191    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    /// Assess memory impact of a closure
204    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    /// Calculate closure memory footprint
215    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    /// Estimate heap usage from captures
241    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    /// Check if a type is typically heap-allocated
251    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    /// Analyze optimization potential for captures
261    fn analyze_optimization_potential(&self, captures: &[CaptureInfo]) -> OptimizationPotential {
262        let mut suggestions = Vec::new();
263        let mut potential_savings = 0;
264
265        // Check for large by-value captures
266        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        // Check for unnecessary mutable captures
277        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        // Check for potential move optimizations
286        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    /// Calculate capture statistics
312    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    /// Generate optimization suggestions
349    fn generate_optimization_suggestions(
350        &self,
351        closures: &[DetectedClosure],
352    ) -> Vec<OptimizationSuggestion> {
353        let mut suggestions = Vec::new();
354
355        // Analyze memory usage patterns
356        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        // Analyze closure types
374        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    /// Analyze capture lifetimes
394    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/// Information about a closure
404#[derive(Debug, Clone, Serialize, Deserialize)]
405pub struct ClosureInfo {
406    /// Closure pointer
407    pub ptr: usize,
408    /// Captured variables
409    pub captures: Vec<CaptureInfo>,
410    /// Creation timestamp
411    pub creation_timestamp: u64,
412    /// Thread ID
413    pub thread_id: String,
414    /// Call site
415    pub call_site: String,
416    /// Closure memory footprint
417    pub memory_footprint: ClosureFootprint,
418    /// Optimization potential
419    pub optimization_potential: OptimizationPotential,
420}
421
422/// Information about a captured variable
423#[derive(Debug, Clone, Serialize, Deserialize)]
424pub struct CaptureInfo {
425    /// Variable name
426    pub var_name: String,
427    /// Variable pointer
428    pub var_ptr: usize,
429    /// Capture mode
430    pub mode: CaptureMode,
431    /// Variable type
432    pub var_type: String,
433    /// Variable size
434    pub size: usize,
435    /// Lifetime bound
436    pub lifetime_bound: Option<String>,
437}
438
439/// Modes of variable capture
440#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
441pub enum CaptureMode {
442    /// Capture by value    
443    ByValue,
444    /// Capture by reference
445    ByReference,
446    /// Capture by mutable reference
447    ByMutableReference,
448}
449
450/// Closure memory footprint analysis
451#[derive(Debug, Clone, Serialize, Deserialize)]
452pub struct ClosureFootprint {
453    /// Total memory footprint
454    pub total_size: usize,
455    /// Number of captured variables
456    pub capture_count: usize,
457    /// Number of captured variables by value
458    pub by_value_count: usize,
459    /// Number of captured variables by reference
460    pub by_ref_count: usize,
461    /// Number of captured variables by mutable reference
462    pub by_mut_ref_count: usize,
463    /// Estimated heap usage
464    pub estimated_heap_usage: usize,
465}
466
467/// Optimization potential analysis
468#[derive(Debug, Clone, Serialize, Deserialize)]
469pub struct OptimizationPotential {
470    /// Optimization level
471    pub level: OptimizationLevel,
472    /// Potential savings in bytes
473    pub potential_savings: usize,
474    /// Optimization suggestions
475    pub suggestions: Vec<String>,
476}
477
478/// Levels of optimization potential
479#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
480pub enum OptimizationLevel {
481    /// No optimization potential
482    None,
483    /// Low optimization potential
484    Low,
485    /// Medium optimization potential
486    Medium,
487    /// High optimization potential
488    High,
489}
490
491/// Capture event tracking
492#[derive(Debug, Clone, Serialize, Deserialize)]
493pub struct CaptureEvent {
494    /// Closure pointer
495    pub closure_ptr: usize,
496    /// Captured variable
497    pub captured_var: CaptureInfo,
498    /// Capture event type
499    pub event_type: CaptureEventType,
500    /// Timestamp
501    pub timestamp: u64,
502}
503
504/// Types of capture    events
505#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
506pub enum CaptureEventType {
507    /// Capture event
508    Captured,
509    /// Release event
510    Released,
511}
512
513/// Detected closure in allocation analysis
514#[derive(Debug, Clone, Serialize, Deserialize)]
515pub struct DetectedClosure {
516    /// Closure pointer
517    pub ptr: usize,
518    /// Closure type name
519    pub type_name: String,
520    /// Closure size
521    pub size: usize,
522    /// Estimated number of captures
523    pub estimated_captures: usize,
524    /// Closure type
525    pub closure_type: ClosureType,
526    /// Closure creation context
527    pub creation_context: CreationContext,
528    /// Closure memory impact
529    pub memory_impact: MemoryImpact,
530}
531
532/// Types of closures
533#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
534pub enum ClosureType {
535    /// Fn closure
536    Fn,
537    /// FnMut closure
538    FnMut,
539    /// FnOnce closure
540    FnOnce,
541    /// Unknown closure type
542    Unknown,
543}
544
545/// Creation context for closures
546#[derive(Debug, Clone, Serialize, Deserialize)]
547pub struct CreationContext {
548    /// Scope name
549    pub scope_name: Option<String>,
550    /// Thread ID
551    pub thread_id: String,
552    /// Timestamp
553    pub timestamp: u64,
554}
555
556/// Memory impact assessment
557#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
558pub enum MemoryImpact {
559    /// Minimal memory impact
560    Minimal,
561    /// Low memory impact
562    Low,
563    /// Medium memory impact
564    Medium,
565    /// High memory impact
566    High,
567    /// Very high memory impact
568    VeryHigh,
569}
570
571/// Capture statistics
572#[derive(Debug, Clone, Default, Serialize, Deserialize)]
573pub struct CaptureStatistics {
574    /// Total number of closures
575    pub total_closures: usize,
576    /// Total number of captures
577    pub total_captures: usize,
578    /// Average number of captures per closure
579    pub avg_captures_per_closure: f64,
580    /// Total memory usage
581    pub total_memory_usage: usize,
582    /// Captures by mode
583    pub captures_by_mode: HashMap<CaptureMode, usize>,
584    /// Captures by type
585    pub captures_by_type: HashMap<String, usize>,
586}
587
588/// Optimization suggestions
589#[derive(Debug, Clone, Serialize, Deserialize)]
590pub struct OptimizationSuggestion {
591    /// Optimization category
592    pub category: OptimizationCategory,
593    /// Suggestion priority
594    pub priority: SuggestionPriority,
595    /// Suggestion description
596    pub description: String,
597    /// Suggestion recommendation
598    pub recommendation: String,
599    /// Estimated impact
600    pub estimated_impact: String,
601}
602
603/// Categories of optimizations
604#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
605pub enum OptimizationCategory {
606    /// Memory optimization category
607    Memory,
608    /// Performance optimization category
609    Performance,
610    /// Lifetime optimization category
611    Lifetime,
612}
613
614/// Priority levels for suggestions
615#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
616pub enum SuggestionPriority {
617    /// Low priority suggestion
618    Low,
619    /// Medium priority suggestion
620    Medium,
621    /// High priority suggestion
622    High,
623    /// Critical priority suggestion
624    Critical,
625}
626
627/// Lifetime relationship graph
628#[derive(Debug)]
629pub struct LifetimeGraph {
630    /// Relationships between closures and captured variables
631    relationships: HashMap<usize, Vec<LifetimeRelationship>>,
632}
633
634impl Default for LifetimeGraph {
635    fn default() -> Self {
636        Self::new()
637    }
638}
639
640impl LifetimeGraph {
641    /// Create a new lifetime graph
642    pub fn new() -> Self {
643        Self {
644            relationships: HashMap::new(),
645        }
646    }
647
648    /// Add relationships for a closure
649    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    /// Remove a closure and its relationships
663    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    /// Analyze lifetime relationships
676    pub fn analyze_lifetimes(&self) -> LifetimeAnalysis {
677        let mut potential_issues = Vec::new();
678        let mut lifetime_patterns = Vec::new();
679
680        // Analyze for potential lifetime issues
681        for (closure_ptr, relationships) in &self.relationships {
682            // Check for mixed capture modes
683            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            // Analyze patterns
704            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/// Lifetime relationship between closure and captured variable
726#[derive(Debug, Clone)]
727pub struct LifetimeRelationship {
728    /// Captured variable pointer
729    pub captured_var_ptr: usize,
730    /// Capture mode
731    pub capture_mode: CaptureMode,
732    /// Relationship type
733    pub relationship_type: RelationshipType,
734}
735
736/// Types of lifetime relationships
737#[derive(Debug, Clone, PartialEq)]
738pub enum RelationshipType {
739    /// Ownership relationship
740    Ownership,
741    /// Shared borrow relationship
742    SharedBorrow,
743    /// Exclusive borrow relationship
744    ExclusiveBorrow,
745}
746
747/// Lifetime analysis results
748#[derive(Debug, Clone, Default, Serialize, Deserialize)]
749pub struct LifetimeAnalysis {
750    /// Total number of relationships
751    pub total_relationships: usize,
752    /// Potential issues
753    pub potential_issues: Vec<LifetimeIssue>,
754    /// Lifetime patterns
755    pub lifetime_patterns: Vec<LifetimePattern>,
756}
757
758/// Lifetime-related issues
759#[derive(Debug, Clone, Serialize, Deserialize)]
760pub struct LifetimeIssue {
761    /// Closure pointer
762    pub closure_ptr: usize,
763    /// Issue type
764    pub issue_type: LifetimeIssueType,
765    /// Issue description
766    pub description: String,
767    /// Issue severity
768    pub severity: IssueSeverity,
769    /// Suggestion for resolution
770    pub suggestion: String,
771}
772
773/// Types of lifetime issues
774#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
775pub enum LifetimeIssueType {
776    /// Mixed capture mode
777    MixedCaptureMode,
778    /// Potential dangling reference
779    PotentialDanglingReference,
780    /// Unnecessary capture
781    UnnecessaryCapture,
782}
783
784/// Issue severity levels
785#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
786pub enum IssueSeverity {
787    /// Low severity
788    Low,
789    /// Medium severity
790    Medium,
791    /// High severity
792    High,
793    /// Critical severity
794    Critical,
795}
796
797/// Lifetime patterns
798#[derive(Debug, Clone, Serialize, Deserialize)]
799pub struct LifetimePattern {
800    /// Pattern type
801    pub pattern_type: LifetimePatternType,
802    /// Pattern description
803    pub description: String,
804    /// Pattern impact
805    pub impact: PatternImpact,
806}
807
808/// Types of lifetime patterns
809#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
810pub enum LifetimePatternType {
811    /// Many captures
812    ManyCaptures,
813    /// Long-lived closure
814    LongLivedClosure,
815    /// Frequent creation
816    FrequentCreation,
817}
818
819/// Impact of patterns
820#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
821pub enum PatternImpact {
822    /// Low impact
823    Low,
824    /// Medium impact
825    Medium,
826    /// High impact
827    High,
828}
829
830/// Comprehensive closure analysis report
831#[derive(Debug, Clone, Serialize, Deserialize)]
832pub struct ClosureAnalysisReport {
833    /// Detected closures
834    pub detected_closures: Vec<DetectedClosure>,
835    /// Capture statistics
836    pub capture_statistics: CaptureStatistics,
837    /// Optimization suggestions
838    pub optimization_suggestions: Vec<OptimizationSuggestion>,
839    /// Lifetime analysis
840    pub lifetime_analysis: LifetimeAnalysis,
841    /// Analysis timestamp
842    pub analysis_timestamp: u64,
843}
844
845// Utility functions
846
847/// Get current timestamp in nanoseconds
848fn 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
855/// Capture call site information (simplified)
856fn capture_call_site() -> String {
857    // In a real implementation, this would capture more detailed call site info
858    "<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        // Test that analyzer is properly initialized
877        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        // Test that we can create multiple analyzers without issues
884        let analyzer1 = ClosureAnalyzer::new();
885        let analyzer2 = ClosureAnalyzer::new();
886
887        // Each should be independent instances
888        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        // Verify closure was registered
917        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        // Verify capture events were recorded
928        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        // Verify closure exists
951        assert!(analyzer.closures.lock().unwrap().contains_key(&0x6000));
952
953        // Track drop
954        analyzer.track_closure_drop(0x6000);
955
956        // Verify closure was removed
957        assert!(!analyzer.closures.lock().unwrap().contains_key(&0x6000));
958
959        // Verify release events were recorded
960        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        // Test various closure type names
974        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        // Test non-closure types
983        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        // Test heap-allocated types
1047        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        // Test stack-allocated types
1055        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); // No heap-allocated types by value
1099    }
1100
1101    #[test]
1102    fn test_analyze_optimization_potential() {
1103        let analyzer = ClosureAnalyzer::new();
1104
1105        // Test with large by-value capture
1106        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        // Test with small captures
1121        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        // Create test allocations with closure types
1140        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()); // Not a closure
1148
1149        let allocations = vec![alloc1, alloc2, alloc3];
1150
1151        let report = analyzer.analyze_closure_patterns(&allocations);
1152
1153        // Should detect 2 closures
1154        assert_eq!(report.detected_closures.len(), 2);
1155
1156        // Check first detected closure
1157        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); // 32 bytes -> 2 captures
1161        assert_eq!(first_closure.closure_type, ClosureType::Unknown);
1162        assert_eq!(first_closure.memory_impact, MemoryImpact::Low);
1163
1164        // Check second detected closure
1165        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); // 64 bytes -> 8 captures
1169        assert_eq!(second_closure.closure_type, ClosureType::FnOnce);
1170        assert_eq!(second_closure.memory_impact, MemoryImpact::Low);
1171
1172        // Verify timestamp
1173        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        // Verify relationships were added
1202        assert!(graph.relationships.contains_key(&0x5000));
1203        let relationships = &graph.relationships[&0x5000];
1204        assert_eq!(relationships.len(), 2);
1205
1206        // Check relationship types
1207        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        // Test removal
1217        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        // Add closure with mixed capture modes (should trigger issue)
1226        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        // Create closure with many captures
1263        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        // Register multiple closures with different characteristics
1487        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        // Create allocations for analysis
1509        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        // Verify comprehensive analysis
1520        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        // Check that high memory usage triggered optimization suggestions
1526        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        // Test concurrent access
1543        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        // All closures should be dropped
1566        assert!(analyzer.closures.lock().unwrap().is_empty());
1567
1568        // Should have capture and release events
1569        let events = analyzer.capture_events.lock().unwrap();
1570        assert_eq!(events.len(), 8); // 4 captures + 4 releases
1571    }
1572}