Skip to main content

torsh_jit/
error_diagnostics.rs

1//! Error diagnostics for JIT compilation
2//!
3//! This module provides comprehensive error diagnostics capabilities for JIT compilation,
4//! including detailed error messages, source location tracking, and recovery suggestions.
5
6// Framework infrastructure - components designed for future use
7#![allow(dead_code)]
8#![allow(unexpected_cfgs)]
9use crate::{JitError, JitResult};
10use std::collections::HashMap;
11use std::fmt;
12use std::time::Instant;
13
14/// Error diagnostics manager
15#[derive(Debug)]
16pub struct ErrorDiagnosticsManager {
17    /// Diagnostic configuration
18    config: DiagnosticsConfig,
19
20    /// Error history
21    error_history: Vec<DiagnosticError>,
22
23    /// Error patterns for analysis
24    error_patterns: HashMap<String, ErrorPattern>,
25
26    /// Recovery suggestions database
27    recovery_suggestions: HashMap<ErrorCategory, Vec<RecoverySuggestion>>,
28
29    /// Context information
30    context_stack: Vec<DiagnosticContext>,
31
32    /// Statistics
33    stats: DiagnosticsStats,
34}
35
36/// Diagnostic error with enhanced information
37#[derive(Debug)]
38pub struct DiagnosticError {
39    /// Unique error ID
40    pub id: String,
41
42    /// Error timestamp
43    pub timestamp: Instant,
44
45    /// Error category
46    pub category: ErrorCategory,
47
48    /// Error severity
49    pub severity: ErrorSeverity,
50
51    /// Error message
52    pub message: String,
53
54    /// Source location
55    pub source_location: Option<SourceLocation>,
56
57    /// Stack trace
58    pub stack_trace: Vec<StackFrame>,
59
60    /// Error context
61    pub context: DiagnosticContext,
62
63    /// Related errors
64    pub related_errors: Vec<String>,
65
66    /// Recovery suggestions
67    pub suggestions: Vec<RecoverySuggestion>,
68
69    /// Error metadata
70    pub metadata: HashMap<String, String>,
71
72    /// Underlying error
73    pub underlying_error: Option<Box<JitError>>,
74}
75
76/// Error categories for classification
77#[derive(Debug, Clone, PartialEq, Eq, Hash)]
78pub enum ErrorCategory {
79    /// Graph construction errors
80    GraphConstruction,
81
82    /// Type inference errors
83    TypeInference,
84
85    /// Shape inference errors
86    ShapeInference,
87
88    /// Optimization errors
89    Optimization,
90
91    /// Code generation errors
92    CodeGeneration,
93
94    /// Runtime errors
95    Runtime,
96
97    /// Memory errors
98    Memory,
99
100    /// Resource errors
101    Resource,
102
103    /// User input errors
104    UserInput,
105
106    /// Internal compiler errors
107    Internal,
108
109    /// External dependency errors
110    External,
111}
112
113/// Error severity levels
114#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
115pub enum ErrorSeverity {
116    /// Information messages
117    Info,
118
119    /// Warning messages
120    Warning,
121
122    /// Error messages
123    Error,
124
125    /// Fatal error messages
126    Fatal,
127
128    /// Internal compiler error
129    Ice, // Internal Compiler Error
130}
131
132/// Source location for diagnostics
133#[derive(Debug, Clone)]
134pub struct SourceLocation {
135    /// File path
136    pub file: String,
137
138    /// Line number (1-based)
139    pub line: u32,
140
141    /// Column number (1-based)
142    pub column: u32,
143
144    /// Length of the error span
145    pub length: Option<u32>,
146
147    /// Source code snippet
148    pub snippet: Option<String>,
149}
150
151/// Stack frame for error traces
152#[derive(Debug, Clone)]
153pub struct StackFrame {
154    /// Function name
155    pub function: String,
156
157    /// File path
158    pub file: Option<String>,
159
160    /// Line number
161    pub line: Option<u32>,
162
163    /// Address
164    pub address: Option<u64>,
165
166    /// Module name
167    pub module: Option<String>,
168}
169
170/// Diagnostic context
171#[derive(Debug, Clone)]
172pub struct DiagnosticContext {
173    /// Operation being performed
174    pub operation: String,
175
176    /// Input description
177    pub input: String,
178
179    /// Expected result
180    pub expected: Option<String>,
181
182    /// Actual result
183    pub actual: Option<String>,
184
185    /// Environment information
186    pub environment: EnvironmentInfo,
187
188    /// Additional context data
189    pub data: HashMap<String, String>,
190}
191
192/// Environment information
193#[derive(Debug, Clone)]
194pub struct EnvironmentInfo {
195    /// Rust version
196    pub rust_version: String,
197
198    /// ToRSh version
199    pub torsh_version: String,
200
201    /// Target architecture
202    pub target_arch: String,
203
204    /// Operating system
205    pub target_os: String,
206
207    /// Available memory
208    pub available_memory: Option<u64>,
209
210    /// CPU information
211    pub cpu_info: Option<String>,
212
213    /// GPU information
214    pub gpu_info: Option<String>,
215}
216
217/// Error pattern for recognition
218#[derive(Debug, Clone)]
219pub struct ErrorPattern {
220    /// Pattern name
221    pub name: String,
222
223    /// Pattern description
224    pub description: String,
225
226    /// Matching criteria
227    pub criteria: Vec<MatchCriterion>,
228
229    /// Common causes
230    pub common_causes: Vec<String>,
231
232    /// Suggested solutions
233    pub solutions: Vec<RecoverySuggestion>,
234
235    /// Frequency of occurrence
236    pub frequency: u64,
237}
238
239/// Criteria for matching error patterns
240#[derive(Debug, Clone)]
241pub enum MatchCriterion {
242    /// Message contains text
243    MessageContains(String),
244
245    /// Error category matches
246    CategoryEquals(ErrorCategory),
247
248    /// Source location matches pattern
249    LocationMatches(String),
250
251    /// Stack trace contains function
252    StackContains(String),
253
254    /// Custom matcher
255    Custom(fn(&DiagnosticError) -> bool),
256}
257
258/// Recovery suggestion
259#[derive(Debug, Clone)]
260pub struct RecoverySuggestion {
261    /// Suggestion type
262    pub suggestion_type: SuggestionType,
263
264    /// Suggestion message
265    pub message: String,
266
267    /// Detailed explanation
268    pub explanation: Option<String>,
269
270    /// Code example (if applicable)
271    pub code_example: Option<String>,
272
273    /// Link to documentation
274    pub doc_link: Option<String>,
275
276    /// Confidence level (0.0 - 1.0)
277    pub confidence: f32,
278
279    /// Automatic fix available
280    pub auto_fix: Option<AutoFix>,
281}
282
283/// Types of suggestions
284#[derive(Debug, Clone)]
285pub enum SuggestionType {
286    /// Quick fix
287    QuickFix,
288
289    /// Code change
290    CodeChange,
291
292    /// Configuration change
293    ConfigChange,
294
295    /// Environment setup
296    EnvironmentSetup,
297
298    /// Documentation reference
299    Documentation,
300
301    /// Workaround
302    Workaround,
303
304    /// Investigation required
305    Investigation,
306}
307
308/// Automatic fix information
309#[derive(Debug, Clone)]
310pub struct AutoFix {
311    /// Fix description
312    pub description: String,
313
314    /// Fix function
315    pub fix_fn: fn(&DiagnosticError) -> JitResult<()>,
316
317    /// Side effects
318    pub side_effects: Vec<String>,
319
320    /// Requires user confirmation
321    pub requires_confirmation: bool,
322}
323
324/// Diagnostics configuration
325#[derive(Debug, Clone)]
326pub struct DiagnosticsConfig {
327    /// Enable error diagnostics
328    pub enabled: bool,
329
330    /// Maximum error history size
331    pub max_history_size: usize,
332
333    /// Enable stack trace collection
334    pub collect_stack_traces: bool,
335
336    /// Enable source snippet extraction
337    pub extract_source_snippets: bool,
338
339    /// Error reporting level
340    pub reporting_level: ErrorSeverity,
341
342    /// Enable pattern matching
343    pub enable_pattern_matching: bool,
344
345    /// Enable recovery suggestions
346    pub enable_suggestions: bool,
347
348    /// Maximum suggestions per error
349    pub max_suggestions: usize,
350
351    /// Color output for terminal
352    pub color_output: bool,
353
354    /// Verbose output
355    pub verbose: bool,
356}
357
358/// Diagnostics statistics
359#[derive(Debug, Clone, Default)]
360pub struct DiagnosticsStats {
361    /// Total errors recorded
362    pub total_errors: u64,
363
364    /// Errors by category
365    pub errors_by_category: HashMap<ErrorCategory, u64>,
366
367    /// Errors by severity
368    pub errors_by_severity: HashMap<ErrorSeverity, u64>,
369
370    /// Pattern matches
371    pub pattern_matches: u64,
372
373    /// Suggestions provided
374    pub suggestions_provided: u64,
375
376    /// Auto-fixes applied
377    pub auto_fixes_applied: u64,
378}
379
380/// Error formatter for different output formats
381pub struct ErrorFormatter {
382    /// Formatting configuration
383    config: FormatterConfig,
384}
385
386/// Formatter configuration
387#[derive(Debug, Clone)]
388pub struct FormatterConfig {
389    /// Include source snippets
390    pub include_source: bool,
391
392    /// Include stack traces
393    pub include_stack_trace: bool,
394
395    /// Include suggestions
396    pub include_suggestions: bool,
397
398    /// Use colors
399    pub use_colors: bool,
400
401    /// Maximum line length
402    pub max_line_length: usize,
403
404    /// Indentation size
405    pub indent_size: usize,
406}
407
408impl Default for DiagnosticsConfig {
409    fn default() -> Self {
410        Self {
411            enabled: true,
412            max_history_size: 1000,
413            collect_stack_traces: true,
414            extract_source_snippets: true,
415            reporting_level: ErrorSeverity::Warning,
416            enable_pattern_matching: true,
417            enable_suggestions: true,
418            max_suggestions: 5,
419            color_output: true,
420            verbose: false,
421        }
422    }
423}
424
425impl Default for FormatterConfig {
426    fn default() -> Self {
427        Self {
428            include_source: true,
429            include_stack_trace: true,
430            include_suggestions: true,
431            use_colors: true,
432            max_line_length: 120,
433            indent_size: 2,
434        }
435    }
436}
437
438impl ErrorDiagnosticsManager {
439    /// Create a new error diagnostics manager
440    pub fn new(config: DiagnosticsConfig) -> Self {
441        let mut manager = Self {
442            config,
443            error_history: Vec::new(),
444            error_patterns: HashMap::new(),
445            recovery_suggestions: HashMap::new(),
446            context_stack: Vec::new(),
447            stats: DiagnosticsStats::default(),
448        };
449
450        manager.initialize_default_patterns();
451        manager.initialize_default_suggestions();
452        manager
453    }
454
455    /// Create a new manager with default configuration
456    pub fn with_defaults() -> Self {
457        Self::new(DiagnosticsConfig::default())
458    }
459
460    /// Record an error with diagnostics
461    pub fn record_error(&mut self, error: JitError) -> DiagnosticError {
462        let mut diagnostic_error = self.create_diagnostic_error(error);
463
464        // Update statistics
465        self.stats.total_errors += 1;
466        *self
467            .stats
468            .errors_by_category
469            .entry(diagnostic_error.category.clone())
470            .or_insert(0) += 1;
471        *self
472            .stats
473            .errors_by_severity
474            .entry(diagnostic_error.severity.clone())
475            .or_insert(0) += 1;
476
477        // Try to match error patterns
478        if self.config.enable_pattern_matching {
479            self.match_error_patterns(&diagnostic_error);
480        }
481
482        // Add recovery suggestions
483        if self.config.enable_suggestions {
484            self.add_recovery_suggestions(&mut diagnostic_error);
485        }
486
487        // Store in history (clone for history, but we need to return the original)
488        let diagnostic_error_copy = DiagnosticError {
489            id: diagnostic_error.id.clone(),
490            timestamp: diagnostic_error.timestamp,
491            category: diagnostic_error.category.clone(),
492            severity: diagnostic_error.severity.clone(),
493            message: diagnostic_error.message.clone(),
494            source_location: diagnostic_error.source_location.clone(),
495            stack_trace: diagnostic_error.stack_trace.clone(),
496            context: diagnostic_error.context.clone(),
497            related_errors: diagnostic_error.related_errors.clone(),
498            suggestions: diagnostic_error.suggestions.clone(),
499            metadata: diagnostic_error.metadata.clone(),
500            underlying_error: None, // Don't store the underlying error in history to avoid clone issues
501        };
502
503        if self.error_history.len() >= self.config.max_history_size {
504            self.error_history.remove(0);
505        }
506        self.error_history.push(diagnostic_error_copy);
507
508        diagnostic_error
509    }
510
511    /// Create a diagnostic error from a JIT error
512    fn create_diagnostic_error(&self, error: JitError) -> DiagnosticError {
513        let error_id = format!("err_{}", self.stats.total_errors);
514        let category = self.categorize_error(&error);
515        let severity = self.determine_severity(&error);
516        let message = error.to_string();
517
518        let context = self
519            .context_stack
520            .last()
521            .cloned()
522            .unwrap_or_else(|| DiagnosticContext {
523                operation: "unknown".to_string(),
524                input: "unknown".to_string(),
525                expected: None,
526                actual: None,
527                environment: self.get_environment_info(),
528                data: HashMap::new(),
529            });
530
531        DiagnosticError {
532            id: error_id,
533            timestamp: Instant::now(),
534            category,
535            severity,
536            message,
537            source_location: None,
538            stack_trace: self.collect_stack_trace(),
539            context,
540            related_errors: Vec::new(),
541            suggestions: Vec::new(),
542            metadata: HashMap::new(),
543            underlying_error: Some(Box::new(error)),
544        }
545    }
546
547    /// Categorize an error
548    fn categorize_error(&self, error: &JitError) -> ErrorCategory {
549        match error {
550            JitError::GraphError(_) => ErrorCategory::GraphConstruction,
551            JitError::OptimizationError(_) => ErrorCategory::Optimization,
552            JitError::CodeGenError(_) => ErrorCategory::CodeGeneration,
553            JitError::RuntimeError(_) => ErrorCategory::Runtime,
554            JitError::UnsupportedOp(_) => ErrorCategory::UserInput,
555            JitError::CompilationError(_) => ErrorCategory::CodeGeneration,
556            JitError::AnalysisError(_) => ErrorCategory::TypeInference,
557            JitError::BackendError(_) => ErrorCategory::External,
558            JitError::FusionError(_) => ErrorCategory::Optimization,
559            JitError::AbstractInterpretationError(_) => ErrorCategory::TypeInference,
560            JitError::NotImplemented(_) => ErrorCategory::UserInput,
561        }
562    }
563
564    /// Determine error severity
565    fn determine_severity(&self, error: &JitError) -> ErrorSeverity {
566        match error {
567            JitError::GraphError(_) => ErrorSeverity::Error,
568            JitError::OptimizationError(_) => ErrorSeverity::Warning,
569            JitError::CodeGenError(_) => ErrorSeverity::Error,
570            JitError::RuntimeError(_) => ErrorSeverity::Error,
571            JitError::UnsupportedOp(_) => ErrorSeverity::Error,
572            JitError::CompilationError(_) => ErrorSeverity::Error,
573            JitError::AnalysisError(_) => ErrorSeverity::Warning,
574            JitError::BackendError(_) => ErrorSeverity::Fatal,
575            JitError::FusionError(_) => ErrorSeverity::Warning,
576            JitError::AbstractInterpretationError(_) => ErrorSeverity::Warning,
577            JitError::NotImplemented(_) => ErrorSeverity::Error,
578        }
579    }
580
581    /// Collect stack trace
582    fn collect_stack_trace(&self) -> Vec<StackFrame> {
583        if !self.config.collect_stack_traces {
584            return Vec::new();
585        }
586
587        // Use std::backtrace if available, otherwise provide basic frame
588        #[cfg(feature = "std_backtrace")]
589        {
590            use std::backtrace::{Backtrace, BacktraceStatus};
591            let bt = Backtrace::capture();
592            if bt.status() == BacktraceStatus::Captured {
593                // Parse backtrace frames
594                let bt_str = format!("{:?}", bt);
595                return self.parse_backtrace_string(&bt_str);
596            }
597        }
598
599        // Fallback: collect limited stack trace using thread info
600        let mut frames = Vec::new();
601
602        // Add current thread information
603        let thread = std::thread::current();
604        frames.push(StackFrame {
605            function: thread.name().unwrap_or("unknown").to_string(),
606            file: None,
607            line: None,
608            address: None,
609            module: Some("torsh_jit".to_string()),
610        });
611
612        frames
613    }
614
615    /// Parse backtrace string into stack frames
616    #[cfg(feature = "std_backtrace")]
617    fn parse_backtrace_string(&self, backtrace: &str) -> Vec<StackFrame> {
618        let mut frames = Vec::new();
619        for line in backtrace.lines().take(20) {
620            // Simple parsing - can be enhanced
621            if let Some(function) = line.split("::").last() {
622                frames.push(StackFrame {
623                    function: function.trim().to_string(),
624                    file: None,
625                    line: None,
626                    address: None,
627                    module: Some("torsh_jit".to_string()),
628                });
629            }
630        }
631        frames
632    }
633
634    /// Get environment information
635    fn get_environment_info(&self) -> EnvironmentInfo {
636        EnvironmentInfo {
637            rust_version: self.get_rust_version(),
638            torsh_version: env!("CARGO_PKG_VERSION").to_string(),
639            target_arch: std::env::consts::ARCH.to_string(),
640            target_os: std::env::consts::OS.to_string(),
641            available_memory: self.get_available_memory(),
642            cpu_info: self.get_cpu_info(),
643            gpu_info: self.get_gpu_info(),
644        }
645    }
646
647    /// Get Rust version
648    fn get_rust_version(&self) -> String {
649        // Try to get from rustc --version
650        std::env::var("RUSTC_VERSION")
651            .unwrap_or_else(|_| env!("CARGO_PKG_RUST_VERSION").to_string())
652    }
653
654    /// Get available memory information in bytes
655    fn get_available_memory(&self) -> Option<u64> {
656        #[cfg(target_os = "linux")]
657        {
658            if let Ok(contents) = std::fs::read_to_string("/proc/meminfo") {
659                for line in contents.lines() {
660                    if line.starts_with("MemTotal:") {
661                        if let Some(kb_str) = line.split_whitespace().nth(1) {
662                            if let Ok(kb) = kb_str.parse::<u64>() {
663                                return Some(kb * 1024); // Convert KB to bytes
664                            }
665                        }
666                    }
667                }
668            }
669        }
670
671        #[cfg(target_os = "macos")]
672        {
673            use std::process::Command;
674            if let Ok(output) = Command::new("sysctl").arg("hw.memsize").output() {
675                if let Ok(text) = String::from_utf8(output.stdout) {
676                    if let Some(size) = text.split(':').nth(1) {
677                        if let Ok(bytes) = size.trim().parse::<u64>() {
678                            return Some(bytes);
679                        }
680                    }
681                }
682            }
683        }
684
685        None
686    }
687
688    /// Get CPU information
689    fn get_cpu_info(&self) -> Option<String> {
690        #[cfg(target_os = "linux")]
691        {
692            if let Ok(contents) = std::fs::read_to_string("/proc/cpuinfo") {
693                for line in contents.lines() {
694                    if line.starts_with("model name") {
695                        if let Some(name) = line.split(':').nth(1) {
696                            return Some(name.trim().to_string());
697                        }
698                    }
699                }
700            }
701        }
702
703        #[cfg(target_os = "macos")]
704        {
705            use std::process::Command;
706            if let Ok(output) = Command::new("sysctl")
707                .arg("-n")
708                .arg("machdep.cpu.brand_string")
709                .output()
710            {
711                if let Ok(cpu) = String::from_utf8(output.stdout) {
712                    return Some(cpu.trim().to_string());
713                }
714            }
715        }
716
717        Some(format!("{} core(s)", num_cpus::get()))
718    }
719
720    /// Get GPU information
721    fn get_gpu_info(&self) -> Option<String> {
722        #[cfg(feature = "gpu")]
723        {
724            // Would integrate with torsh-backend-cuda for actual GPU info
725            // For now, return basic placeholder
726            Some("GPU support enabled".to_string())
727        }
728
729        #[cfg(not(feature = "gpu"))]
730        {
731            None
732        }
733    }
734
735    /// Match error patterns
736    fn match_error_patterns(&mut self, error: &DiagnosticError) {
737        let mut matched_patterns = Vec::new();
738        for (pattern_name, pattern) in &self.error_patterns {
739            if self.matches_pattern(error, pattern) {
740                self.stats.pattern_matches += 1;
741                matched_patterns.push((pattern_name.clone(), pattern.clone()));
742            }
743        }
744
745        // Apply matched patterns (need to drop the immutable borrow first)
746        // This is a simplified version - in production we'd integrate this better
747        for (_pattern_name, _pattern) in matched_patterns {
748            // Pattern handling would be applied here
749        }
750    }
751
752    /// Apply pattern-specific error handling
753    fn apply_pattern_handling(
754        &mut self,
755        error: &mut DiagnosticError,
756        pattern_name: &str,
757        pattern: &ErrorPattern,
758    ) {
759        // Add pattern-specific suggestions (without checking for duplicates)
760        for suggestion in &pattern.solutions {
761            error.suggestions.push(suggestion.clone());
762        }
763
764        // Add common causes as related information
765        for cause in &pattern.common_causes {
766            error
767                .related_errors
768                .push(format!("Common cause: {}", cause));
769        }
770
771        // Add context information
772        if let Some(ref ctx) = self.context_stack.last() {
773            error.related_errors.push(format!(
774                "Pattern '{}' matched in context: {}",
775                pattern_name, ctx.operation
776            ));
777        }
778
779        // Record pattern match for statistics
780        self.stats.suggestions_provided += pattern.solutions.len() as u64;
781
782        // Adjust error severity if pattern is frequently occurring
783        if pattern.frequency > 100 {
784            // Downgrade frequently occurring errors to warnings
785            if error.severity == ErrorSeverity::Error {
786                error.severity = ErrorSeverity::Warning;
787            }
788        }
789    }
790
791    /// Check if error matches a pattern
792    fn matches_pattern(&self, error: &DiagnosticError, pattern: &ErrorPattern) -> bool {
793        for criterion in &pattern.criteria {
794            match criterion {
795                MatchCriterion::MessageContains(text) => {
796                    if !error.message.contains(text) {
797                        return false;
798                    }
799                }
800                MatchCriterion::CategoryEquals(category) => {
801                    if error.category != *category {
802                        return false;
803                    }
804                }
805                MatchCriterion::LocationMatches(pattern) => {
806                    if let Some(ref location) = error.source_location {
807                        let location_str =
808                            format!("{}:{}:{}", location.file, location.line, location.column);
809                        if !location_str.contains(pattern) {
810                            return false;
811                        }
812                    } else {
813                        return false;
814                    }
815                }
816                MatchCriterion::StackContains(function) => {
817                    if !error
818                        .stack_trace
819                        .iter()
820                        .any(|frame| frame.function.contains(function))
821                    {
822                        return false;
823                    }
824                }
825                MatchCriterion::Custom(matcher) => {
826                    if !matcher(error) {
827                        return false;
828                    }
829                }
830            }
831        }
832        true
833    }
834
835    /// Add recovery suggestions to an error
836    fn add_recovery_suggestions(&mut self, error: &mut DiagnosticError) {
837        if let Some(suggestions) = self.recovery_suggestions.get(&error.category) {
838            for suggestion in suggestions.iter().take(self.config.max_suggestions) {
839                error.suggestions.push(suggestion.clone());
840                self.stats.suggestions_provided += 1;
841            }
842        }
843    }
844
845    /// Initialize default error patterns
846    fn initialize_default_patterns(&mut self) {
847        // Type mismatch pattern
848        let type_mismatch = ErrorPattern {
849            name: "type_mismatch".to_string(),
850            description: "Type mismatch in operation".to_string(),
851            criteria: vec![
852                MatchCriterion::MessageContains("type".to_string()),
853                MatchCriterion::CategoryEquals(ErrorCategory::TypeInference),
854            ],
855            common_causes: vec![
856                "Incorrect input types".to_string(),
857                "Missing type annotations".to_string(),
858            ],
859            solutions: vec![],
860            frequency: 0,
861        };
862
863        self.error_patterns
864            .insert("type_mismatch".to_string(), type_mismatch);
865
866        // Shape mismatch pattern
867        let shape_mismatch = ErrorPattern {
868            name: "shape_mismatch".to_string(),
869            description: "Shape mismatch in tensor operation".to_string(),
870            criteria: vec![
871                MatchCriterion::MessageContains("shape".to_string()),
872                MatchCriterion::CategoryEquals(ErrorCategory::ShapeInference),
873            ],
874            common_causes: vec![
875                "Incompatible tensor shapes".to_string(),
876                "Missing shape information".to_string(),
877            ],
878            solutions: vec![],
879            frequency: 0,
880        };
881
882        self.error_patterns
883            .insert("shape_mismatch".to_string(), shape_mismatch);
884    }
885
886    /// Initialize default recovery suggestions
887    fn initialize_default_suggestions(&mut self) {
888        // Type inference suggestions
889        let type_suggestions = vec![RecoverySuggestion {
890            suggestion_type: SuggestionType::CodeChange,
891            message: "Check input types and add explicit type annotations".to_string(),
892            explanation: Some(
893                "Type inference failed. Consider adding explicit type information.".to_string(),
894            ),
895            code_example: Some("tensor.cast(DType::F32)".to_string()),
896            doc_link: Some("https://docs.rs/torsh/latest/torsh/".to_string()),
897            confidence: 0.8,
898            auto_fix: None,
899        }];
900
901        self.recovery_suggestions
902            .insert(ErrorCategory::TypeInference, type_suggestions);
903
904        // Shape inference suggestions
905        let shape_suggestions = vec![
906            RecoverySuggestion {
907                suggestion_type: SuggestionType::CodeChange,
908                message: "Verify tensor shapes are compatible for the operation".to_string(),
909                explanation: Some("Shape inference failed. Check that tensor dimensions match operation requirements.".to_string()),
910                code_example: Some("tensor.reshape(&[batch_size, channels, height, width])".to_string()),
911                doc_link: Some("https://docs.rs/torsh/latest/torsh/".to_string()),
912                confidence: 0.9,
913                auto_fix: None,
914            },
915        ];
916
917        self.recovery_suggestions
918            .insert(ErrorCategory::ShapeInference, shape_suggestions);
919    }
920
921    /// Push diagnostic context
922    pub fn push_context(&mut self, context: DiagnosticContext) {
923        self.context_stack.push(context);
924    }
925
926    /// Pop diagnostic context
927    pub fn pop_context(&mut self) -> Option<DiagnosticContext> {
928        self.context_stack.pop()
929    }
930
931    /// Get error history
932    pub fn get_error_history(&self) -> &[DiagnosticError] {
933        &self.error_history
934    }
935
936    /// Get statistics
937    pub fn get_stats(&self) -> &DiagnosticsStats {
938        &self.stats
939    }
940
941    /// Format error for display
942    pub fn format_error(&self, error: &DiagnosticError, format_config: &FormatterConfig) -> String {
943        let formatter = ErrorFormatter::new(format_config.clone());
944        formatter.format(error)
945    }
946
947    /// Get similar errors from history
948    pub fn get_similar_errors(&self, error: &DiagnosticError) -> Vec<&DiagnosticError> {
949        self.error_history
950            .iter()
951            .filter(|e| e.category == error.category && e.severity == error.severity)
952            .collect()
953    }
954
955    /// Export diagnostics data
956    pub fn export_diagnostics(&self, output_path: &str) -> JitResult<()> {
957        let diagnostics_data = format!(
958            r#"{{"total_errors": {}, "errors_by_category": {:?}, "patterns": {}}}"#,
959            self.stats.total_errors,
960            self.stats.errors_by_category,
961            self.error_patterns.len()
962        );
963
964        std::fs::write(output_path, diagnostics_data)
965            .map_err(|e| JitError::RuntimeError(format!("Failed to export diagnostics: {}", e)))?;
966
967        Ok(())
968    }
969}
970
971impl ErrorFormatter {
972    /// Create a new error formatter
973    pub fn new(config: FormatterConfig) -> Self {
974        Self { config }
975    }
976
977    /// Format a diagnostic error
978    pub fn format(&self, error: &DiagnosticError) -> String {
979        let mut output = String::new();
980
981        // Header
982        output.push_str(&format!(
983            "{}[{}] {}: {}\n",
984            self.color_for_severity(&error.severity),
985            error.severity.as_str(),
986            error.category.as_str(),
987            error.message
988        ));
989
990        // Source location
991        if let Some(location) = &error.source_location {
992            output.push_str(&format!(
993                "  --> {}:{}:{}\n",
994                location.file, location.line, location.column
995            ));
996
997            if self.config.include_source {
998                if let Some(snippet) = &location.snippet {
999                    output.push_str(&format!("   |\n   | {}\n   |\n", snippet));
1000                }
1001            }
1002        }
1003
1004        // Context
1005        output.push_str(&format!(
1006            "  Context: {} ({})\n",
1007            error.context.operation, error.context.input
1008        ));
1009
1010        // Stack trace
1011        if self.config.include_stack_trace && !error.stack_trace.is_empty() {
1012            output.push_str("  Stack trace:\n");
1013            for frame in &error.stack_trace {
1014                output.push_str(&format!(
1015                    "    at {} ({}:{})\n",
1016                    frame.function,
1017                    frame.file.as_ref().unwrap_or(&"unknown".to_string()),
1018                    frame.line.unwrap_or(0)
1019                ));
1020            }
1021        }
1022
1023        // Suggestions
1024        if self.config.include_suggestions && !error.suggestions.is_empty() {
1025            output.push_str("  Suggestions:\n");
1026            for suggestion in &error.suggestions {
1027                output.push_str(&format!("    - {}\n", suggestion.message));
1028                if let Some(explanation) = &suggestion.explanation {
1029                    output.push_str(&format!("      {}\n", explanation));
1030                }
1031            }
1032        }
1033
1034        output.push_str(&self.reset_color());
1035        output
1036    }
1037
1038    /// Get color for severity level
1039    fn color_for_severity(&self, severity: &ErrorSeverity) -> &str {
1040        if !self.config.use_colors {
1041            return "";
1042        }
1043
1044        match severity {
1045            ErrorSeverity::Info => "\x1b[36m",    // Cyan
1046            ErrorSeverity::Warning => "\x1b[33m", // Yellow
1047            ErrorSeverity::Error => "\x1b[31m",   // Red
1048            ErrorSeverity::Fatal => "\x1b[35m",   // Magenta
1049            ErrorSeverity::Ice => "\x1b[41m",     // Red background
1050        }
1051    }
1052
1053    /// Reset color
1054    fn reset_color(&self) -> &str {
1055        if self.config.use_colors {
1056            "\x1b[0m"
1057        } else {
1058            ""
1059        }
1060    }
1061}
1062
1063impl ErrorSeverity {
1064    /// Get string representation
1065    pub fn as_str(&self) -> &str {
1066        match self {
1067            ErrorSeverity::Info => "INFO",
1068            ErrorSeverity::Warning => "WARNING",
1069            ErrorSeverity::Error => "ERROR",
1070            ErrorSeverity::Fatal => "FATAL",
1071            ErrorSeverity::Ice => "ICE",
1072        }
1073    }
1074}
1075
1076impl ErrorCategory {
1077    /// Get string representation
1078    pub fn as_str(&self) -> &str {
1079        match self {
1080            ErrorCategory::GraphConstruction => "GRAPH",
1081            ErrorCategory::TypeInference => "TYPE",
1082            ErrorCategory::ShapeInference => "SHAPE",
1083            ErrorCategory::Optimization => "OPT",
1084            ErrorCategory::CodeGeneration => "CODEGEN",
1085            ErrorCategory::Runtime => "RUNTIME",
1086            ErrorCategory::Memory => "MEMORY",
1087            ErrorCategory::Resource => "RESOURCE",
1088            ErrorCategory::UserInput => "INPUT",
1089            ErrorCategory::Internal => "INTERNAL",
1090            ErrorCategory::External => "EXTERNAL",
1091        }
1092    }
1093}
1094
1095impl fmt::Display for DiagnosticError {
1096    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1097        write!(
1098            f,
1099            "[{}] {}: {}",
1100            self.severity.as_str(),
1101            self.category.as_str(),
1102            self.message
1103        )
1104    }
1105}
1106
1107#[cfg(test)]
1108mod tests {
1109    use super::*;
1110
1111    #[test]
1112    fn test_diagnostics_manager_creation() {
1113        let manager = ErrorDiagnosticsManager::with_defaults();
1114        assert!(manager.config.enabled);
1115        assert_eq!(manager.config.max_history_size, 1000);
1116    }
1117
1118    #[test]
1119    fn test_error_recording() {
1120        let mut manager = ErrorDiagnosticsManager::with_defaults();
1121        let error = JitError::RuntimeError("Test error".to_string());
1122
1123        let diagnostic_error = manager.record_error(error);
1124        assert_eq!(diagnostic_error.category, ErrorCategory::Runtime);
1125        assert_eq!(diagnostic_error.severity, ErrorSeverity::Error);
1126        assert!(diagnostic_error.message.contains("Test error"));
1127    }
1128
1129    #[test]
1130    fn test_error_categorization() {
1131        let manager = ErrorDiagnosticsManager::with_defaults();
1132
1133        let graph_error = JitError::GraphError("Graph error".to_string());
1134        assert_eq!(
1135            manager.categorize_error(&graph_error),
1136            ErrorCategory::GraphConstruction
1137        );
1138
1139        let runtime_error = JitError::RuntimeError("Runtime error".to_string());
1140        assert_eq!(
1141            manager.categorize_error(&runtime_error),
1142            ErrorCategory::Runtime
1143        );
1144    }
1145
1146    #[test]
1147    fn test_error_formatting() {
1148        let mut manager = ErrorDiagnosticsManager::with_defaults();
1149        let error = JitError::RuntimeError("Test error".to_string());
1150        let diagnostic_error = manager.record_error(error);
1151
1152        let formatter_config = FormatterConfig::default();
1153        let formatted = manager.format_error(&diagnostic_error, &formatter_config);
1154
1155        assert!(formatted.contains("ERROR"));
1156        assert!(formatted.contains("RUNTIME"));
1157        assert!(formatted.contains("Test error"));
1158    }
1159
1160    #[test]
1161    fn test_context_stack() {
1162        let mut manager = ErrorDiagnosticsManager::with_defaults();
1163
1164        let context = DiagnosticContext {
1165            operation: "test_operation".to_string(),
1166            input: "test_input".to_string(),
1167            expected: None,
1168            actual: None,
1169            environment: manager.get_environment_info(),
1170            data: HashMap::new(),
1171        };
1172
1173        manager.push_context(context.clone());
1174        assert_eq!(manager.context_stack.len(), 1);
1175
1176        let popped = manager.pop_context().unwrap();
1177        assert_eq!(popped.operation, "test_operation");
1178        assert_eq!(manager.context_stack.len(), 0);
1179    }
1180}