1#![allow(dead_code)]
8#![allow(unexpected_cfgs)]
9use crate::{JitError, JitResult};
10use std::collections::HashMap;
11use std::fmt;
12use std::time::Instant;
13
14#[derive(Debug)]
16pub struct ErrorDiagnosticsManager {
17 config: DiagnosticsConfig,
19
20 error_history: Vec<DiagnosticError>,
22
23 error_patterns: HashMap<String, ErrorPattern>,
25
26 recovery_suggestions: HashMap<ErrorCategory, Vec<RecoverySuggestion>>,
28
29 context_stack: Vec<DiagnosticContext>,
31
32 stats: DiagnosticsStats,
34}
35
36#[derive(Debug)]
38pub struct DiagnosticError {
39 pub id: String,
41
42 pub timestamp: Instant,
44
45 pub category: ErrorCategory,
47
48 pub severity: ErrorSeverity,
50
51 pub message: String,
53
54 pub source_location: Option<SourceLocation>,
56
57 pub stack_trace: Vec<StackFrame>,
59
60 pub context: DiagnosticContext,
62
63 pub related_errors: Vec<String>,
65
66 pub suggestions: Vec<RecoverySuggestion>,
68
69 pub metadata: HashMap<String, String>,
71
72 pub underlying_error: Option<Box<JitError>>,
74}
75
76#[derive(Debug, Clone, PartialEq, Eq, Hash)]
78pub enum ErrorCategory {
79 GraphConstruction,
81
82 TypeInference,
84
85 ShapeInference,
87
88 Optimization,
90
91 CodeGeneration,
93
94 Runtime,
96
97 Memory,
99
100 Resource,
102
103 UserInput,
105
106 Internal,
108
109 External,
111}
112
113#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
115pub enum ErrorSeverity {
116 Info,
118
119 Warning,
121
122 Error,
124
125 Fatal,
127
128 Ice, }
131
132#[derive(Debug, Clone)]
134pub struct SourceLocation {
135 pub file: String,
137
138 pub line: u32,
140
141 pub column: u32,
143
144 pub length: Option<u32>,
146
147 pub snippet: Option<String>,
149}
150
151#[derive(Debug, Clone)]
153pub struct StackFrame {
154 pub function: String,
156
157 pub file: Option<String>,
159
160 pub line: Option<u32>,
162
163 pub address: Option<u64>,
165
166 pub module: Option<String>,
168}
169
170#[derive(Debug, Clone)]
172pub struct DiagnosticContext {
173 pub operation: String,
175
176 pub input: String,
178
179 pub expected: Option<String>,
181
182 pub actual: Option<String>,
184
185 pub environment: EnvironmentInfo,
187
188 pub data: HashMap<String, String>,
190}
191
192#[derive(Debug, Clone)]
194pub struct EnvironmentInfo {
195 pub rust_version: String,
197
198 pub torsh_version: String,
200
201 pub target_arch: String,
203
204 pub target_os: String,
206
207 pub available_memory: Option<u64>,
209
210 pub cpu_info: Option<String>,
212
213 pub gpu_info: Option<String>,
215}
216
217#[derive(Debug, Clone)]
219pub struct ErrorPattern {
220 pub name: String,
222
223 pub description: String,
225
226 pub criteria: Vec<MatchCriterion>,
228
229 pub common_causes: Vec<String>,
231
232 pub solutions: Vec<RecoverySuggestion>,
234
235 pub frequency: u64,
237}
238
239#[derive(Debug, Clone)]
241pub enum MatchCriterion {
242 MessageContains(String),
244
245 CategoryEquals(ErrorCategory),
247
248 LocationMatches(String),
250
251 StackContains(String),
253
254 Custom(fn(&DiagnosticError) -> bool),
256}
257
258#[derive(Debug, Clone)]
260pub struct RecoverySuggestion {
261 pub suggestion_type: SuggestionType,
263
264 pub message: String,
266
267 pub explanation: Option<String>,
269
270 pub code_example: Option<String>,
272
273 pub doc_link: Option<String>,
275
276 pub confidence: f32,
278
279 pub auto_fix: Option<AutoFix>,
281}
282
283#[derive(Debug, Clone)]
285pub enum SuggestionType {
286 QuickFix,
288
289 CodeChange,
291
292 ConfigChange,
294
295 EnvironmentSetup,
297
298 Documentation,
300
301 Workaround,
303
304 Investigation,
306}
307
308#[derive(Debug, Clone)]
310pub struct AutoFix {
311 pub description: String,
313
314 pub fix_fn: fn(&DiagnosticError) -> JitResult<()>,
316
317 pub side_effects: Vec<String>,
319
320 pub requires_confirmation: bool,
322}
323
324#[derive(Debug, Clone)]
326pub struct DiagnosticsConfig {
327 pub enabled: bool,
329
330 pub max_history_size: usize,
332
333 pub collect_stack_traces: bool,
335
336 pub extract_source_snippets: bool,
338
339 pub reporting_level: ErrorSeverity,
341
342 pub enable_pattern_matching: bool,
344
345 pub enable_suggestions: bool,
347
348 pub max_suggestions: usize,
350
351 pub color_output: bool,
353
354 pub verbose: bool,
356}
357
358#[derive(Debug, Clone, Default)]
360pub struct DiagnosticsStats {
361 pub total_errors: u64,
363
364 pub errors_by_category: HashMap<ErrorCategory, u64>,
366
367 pub errors_by_severity: HashMap<ErrorSeverity, u64>,
369
370 pub pattern_matches: u64,
372
373 pub suggestions_provided: u64,
375
376 pub auto_fixes_applied: u64,
378}
379
380pub struct ErrorFormatter {
382 config: FormatterConfig,
384}
385
386#[derive(Debug, Clone)]
388pub struct FormatterConfig {
389 pub include_source: bool,
391
392 pub include_stack_trace: bool,
394
395 pub include_suggestions: bool,
397
398 pub use_colors: bool,
400
401 pub max_line_length: usize,
403
404 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 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 pub fn with_defaults() -> Self {
457 Self::new(DiagnosticsConfig::default())
458 }
459
460 pub fn record_error(&mut self, error: JitError) -> DiagnosticError {
462 let mut diagnostic_error = self.create_diagnostic_error(error);
463
464 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 if self.config.enable_pattern_matching {
479 self.match_error_patterns(&diagnostic_error);
480 }
481
482 if self.config.enable_suggestions {
484 self.add_recovery_suggestions(&mut diagnostic_error);
485 }
486
487 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, };
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 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 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 }
561 }
562
563 fn determine_severity(&self, error: &JitError) -> ErrorSeverity {
565 match error {
566 JitError::GraphError(_) => ErrorSeverity::Error,
567 JitError::OptimizationError(_) => ErrorSeverity::Warning,
568 JitError::CodeGenError(_) => ErrorSeverity::Error,
569 JitError::RuntimeError(_) => ErrorSeverity::Error,
570 JitError::UnsupportedOp(_) => ErrorSeverity::Error,
571 JitError::CompilationError(_) => ErrorSeverity::Error,
572 JitError::AnalysisError(_) => ErrorSeverity::Warning,
573 JitError::BackendError(_) => ErrorSeverity::Fatal,
574 JitError::FusionError(_) => ErrorSeverity::Warning,
575 JitError::AbstractInterpretationError(_) => ErrorSeverity::Warning,
576 }
577 }
578
579 fn collect_stack_trace(&self) -> Vec<StackFrame> {
581 if !self.config.collect_stack_traces {
582 return Vec::new();
583 }
584
585 #[cfg(feature = "std_backtrace")]
587 {
588 use std::backtrace::{Backtrace, BacktraceStatus};
589 let bt = Backtrace::capture();
590 if bt.status() == BacktraceStatus::Captured {
591 let bt_str = format!("{:?}", bt);
593 return self.parse_backtrace_string(&bt_str);
594 }
595 }
596
597 let mut frames = Vec::new();
599
600 let thread = std::thread::current();
602 frames.push(StackFrame {
603 function: thread.name().unwrap_or("unknown").to_string(),
604 file: None,
605 line: None,
606 address: None,
607 module: Some("torsh_jit".to_string()),
608 });
609
610 frames
611 }
612
613 #[cfg(feature = "std_backtrace")]
615 fn parse_backtrace_string(&self, backtrace: &str) -> Vec<StackFrame> {
616 let mut frames = Vec::new();
617 for line in backtrace.lines().take(20) {
618 if let Some(function) = line.split("::").last() {
620 frames.push(StackFrame {
621 function: function.trim().to_string(),
622 file: None,
623 line: None,
624 address: None,
625 module: Some("torsh_jit".to_string()),
626 });
627 }
628 }
629 frames
630 }
631
632 fn get_environment_info(&self) -> EnvironmentInfo {
634 EnvironmentInfo {
635 rust_version: self.get_rust_version(),
636 torsh_version: env!("CARGO_PKG_VERSION").to_string(),
637 target_arch: std::env::consts::ARCH.to_string(),
638 target_os: std::env::consts::OS.to_string(),
639 available_memory: self.get_available_memory(),
640 cpu_info: self.get_cpu_info(),
641 gpu_info: self.get_gpu_info(),
642 }
643 }
644
645 fn get_rust_version(&self) -> String {
647 std::env::var("RUSTC_VERSION")
649 .unwrap_or_else(|_| env!("CARGO_PKG_RUST_VERSION").to_string())
650 }
651
652 fn get_available_memory(&self) -> Option<u64> {
654 #[cfg(target_os = "linux")]
655 {
656 if let Ok(contents) = std::fs::read_to_string("/proc/meminfo") {
657 for line in contents.lines() {
658 if line.starts_with("MemTotal:") {
659 if let Some(kb_str) = line.split_whitespace().nth(1) {
660 if let Ok(kb) = kb_str.parse::<u64>() {
661 return Some(kb * 1024); }
663 }
664 }
665 }
666 }
667 }
668
669 #[cfg(target_os = "macos")]
670 {
671 use std::process::Command;
672 if let Ok(output) = Command::new("sysctl").arg("hw.memsize").output() {
673 if let Ok(text) = String::from_utf8(output.stdout) {
674 if let Some(size) = text.split(':').nth(1) {
675 if let Ok(bytes) = size.trim().parse::<u64>() {
676 return Some(bytes);
677 }
678 }
679 }
680 }
681 }
682
683 None
684 }
685
686 fn get_cpu_info(&self) -> Option<String> {
688 #[cfg(target_os = "linux")]
689 {
690 if let Ok(contents) = std::fs::read_to_string("/proc/cpuinfo") {
691 for line in contents.lines() {
692 if line.starts_with("model name") {
693 if let Some(name) = line.split(':').nth(1) {
694 return Some(name.trim().to_string());
695 }
696 }
697 }
698 }
699 }
700
701 #[cfg(target_os = "macos")]
702 {
703 use std::process::Command;
704 if let Ok(output) = Command::new("sysctl")
705 .arg("-n")
706 .arg("machdep.cpu.brand_string")
707 .output()
708 {
709 if let Ok(cpu) = String::from_utf8(output.stdout) {
710 return Some(cpu.trim().to_string());
711 }
712 }
713 }
714
715 Some(format!("{} core(s)", num_cpus::get()))
716 }
717
718 fn get_gpu_info(&self) -> Option<String> {
720 #[cfg(feature = "gpu")]
721 {
722 Some("GPU support enabled".to_string())
725 }
726
727 #[cfg(not(feature = "gpu"))]
728 {
729 None
730 }
731 }
732
733 fn match_error_patterns(&mut self, error: &DiagnosticError) {
735 let mut matched_patterns = Vec::new();
736 for (pattern_name, pattern) in &self.error_patterns {
737 if self.matches_pattern(error, pattern) {
738 self.stats.pattern_matches += 1;
739 matched_patterns.push((pattern_name.clone(), pattern.clone()));
740 }
741 }
742
743 for (_pattern_name, _pattern) in matched_patterns {
746 }
748 }
749
750 fn apply_pattern_handling(
752 &mut self,
753 error: &mut DiagnosticError,
754 pattern_name: &str,
755 pattern: &ErrorPattern,
756 ) {
757 for suggestion in &pattern.solutions {
759 error.suggestions.push(suggestion.clone());
760 }
761
762 for cause in &pattern.common_causes {
764 error
765 .related_errors
766 .push(format!("Common cause: {}", cause));
767 }
768
769 if let Some(ref ctx) = self.context_stack.last() {
771 error.related_errors.push(format!(
772 "Pattern '{}' matched in context: {}",
773 pattern_name, ctx.operation
774 ));
775 }
776
777 self.stats.suggestions_provided += pattern.solutions.len() as u64;
779
780 if pattern.frequency > 100 {
782 if error.severity == ErrorSeverity::Error {
784 error.severity = ErrorSeverity::Warning;
785 }
786 }
787 }
788
789 fn matches_pattern(&self, error: &DiagnosticError, pattern: &ErrorPattern) -> bool {
791 for criterion in &pattern.criteria {
792 match criterion {
793 MatchCriterion::MessageContains(text) => {
794 if !error.message.contains(text) {
795 return false;
796 }
797 }
798 MatchCriterion::CategoryEquals(category) => {
799 if error.category != *category {
800 return false;
801 }
802 }
803 MatchCriterion::LocationMatches(pattern) => {
804 if let Some(ref location) = error.source_location {
805 let location_str =
806 format!("{}:{}:{}", location.file, location.line, location.column);
807 if !location_str.contains(pattern) {
808 return false;
809 }
810 } else {
811 return false;
812 }
813 }
814 MatchCriterion::StackContains(function) => {
815 if !error
816 .stack_trace
817 .iter()
818 .any(|frame| frame.function.contains(function))
819 {
820 return false;
821 }
822 }
823 MatchCriterion::Custom(matcher) => {
824 if !matcher(error) {
825 return false;
826 }
827 }
828 }
829 }
830 true
831 }
832
833 fn add_recovery_suggestions(&mut self, error: &mut DiagnosticError) {
835 if let Some(suggestions) = self.recovery_suggestions.get(&error.category) {
836 for suggestion in suggestions.iter().take(self.config.max_suggestions) {
837 error.suggestions.push(suggestion.clone());
838 self.stats.suggestions_provided += 1;
839 }
840 }
841 }
842
843 fn initialize_default_patterns(&mut self) {
845 let type_mismatch = ErrorPattern {
847 name: "type_mismatch".to_string(),
848 description: "Type mismatch in operation".to_string(),
849 criteria: vec![
850 MatchCriterion::MessageContains("type".to_string()),
851 MatchCriterion::CategoryEquals(ErrorCategory::TypeInference),
852 ],
853 common_causes: vec![
854 "Incorrect input types".to_string(),
855 "Missing type annotations".to_string(),
856 ],
857 solutions: vec![],
858 frequency: 0,
859 };
860
861 self.error_patterns
862 .insert("type_mismatch".to_string(), type_mismatch);
863
864 let shape_mismatch = ErrorPattern {
866 name: "shape_mismatch".to_string(),
867 description: "Shape mismatch in tensor operation".to_string(),
868 criteria: vec![
869 MatchCriterion::MessageContains("shape".to_string()),
870 MatchCriterion::CategoryEquals(ErrorCategory::ShapeInference),
871 ],
872 common_causes: vec![
873 "Incompatible tensor shapes".to_string(),
874 "Missing shape information".to_string(),
875 ],
876 solutions: vec![],
877 frequency: 0,
878 };
879
880 self.error_patterns
881 .insert("shape_mismatch".to_string(), shape_mismatch);
882 }
883
884 fn initialize_default_suggestions(&mut self) {
886 let type_suggestions = vec![RecoverySuggestion {
888 suggestion_type: SuggestionType::CodeChange,
889 message: "Check input types and add explicit type annotations".to_string(),
890 explanation: Some(
891 "Type inference failed. Consider adding explicit type information.".to_string(),
892 ),
893 code_example: Some("tensor.cast(DType::F32)".to_string()),
894 doc_link: Some("https://docs.rs/torsh/latest/torsh/".to_string()),
895 confidence: 0.8,
896 auto_fix: None,
897 }];
898
899 self.recovery_suggestions
900 .insert(ErrorCategory::TypeInference, type_suggestions);
901
902 let shape_suggestions = vec![
904 RecoverySuggestion {
905 suggestion_type: SuggestionType::CodeChange,
906 message: "Verify tensor shapes are compatible for the operation".to_string(),
907 explanation: Some("Shape inference failed. Check that tensor dimensions match operation requirements.".to_string()),
908 code_example: Some("tensor.reshape(&[batch_size, channels, height, width])".to_string()),
909 doc_link: Some("https://docs.rs/torsh/latest/torsh/".to_string()),
910 confidence: 0.9,
911 auto_fix: None,
912 },
913 ];
914
915 self.recovery_suggestions
916 .insert(ErrorCategory::ShapeInference, shape_suggestions);
917 }
918
919 pub fn push_context(&mut self, context: DiagnosticContext) {
921 self.context_stack.push(context);
922 }
923
924 pub fn pop_context(&mut self) -> Option<DiagnosticContext> {
926 self.context_stack.pop()
927 }
928
929 pub fn get_error_history(&self) -> &[DiagnosticError] {
931 &self.error_history
932 }
933
934 pub fn get_stats(&self) -> &DiagnosticsStats {
936 &self.stats
937 }
938
939 pub fn format_error(&self, error: &DiagnosticError, format_config: &FormatterConfig) -> String {
941 let formatter = ErrorFormatter::new(format_config.clone());
942 formatter.format(error)
943 }
944
945 pub fn get_similar_errors(&self, error: &DiagnosticError) -> Vec<&DiagnosticError> {
947 self.error_history
948 .iter()
949 .filter(|e| e.category == error.category && e.severity == error.severity)
950 .collect()
951 }
952
953 pub fn export_diagnostics(&self, output_path: &str) -> JitResult<()> {
955 let diagnostics_data = format!(
956 r#"{{"total_errors": {}, "errors_by_category": {:?}, "patterns": {}}}"#,
957 self.stats.total_errors,
958 self.stats.errors_by_category,
959 self.error_patterns.len()
960 );
961
962 std::fs::write(output_path, diagnostics_data)
963 .map_err(|e| JitError::RuntimeError(format!("Failed to export diagnostics: {}", e)))?;
964
965 Ok(())
966 }
967}
968
969impl ErrorFormatter {
970 pub fn new(config: FormatterConfig) -> Self {
972 Self { config }
973 }
974
975 pub fn format(&self, error: &DiagnosticError) -> String {
977 let mut output = String::new();
978
979 output.push_str(&format!(
981 "{}[{}] {}: {}\n",
982 self.color_for_severity(&error.severity),
983 error.severity.as_str(),
984 error.category.as_str(),
985 error.message
986 ));
987
988 if let Some(location) = &error.source_location {
990 output.push_str(&format!(
991 " --> {}:{}:{}\n",
992 location.file, location.line, location.column
993 ));
994
995 if self.config.include_source {
996 if let Some(snippet) = &location.snippet {
997 output.push_str(&format!(" |\n | {}\n |\n", snippet));
998 }
999 }
1000 }
1001
1002 output.push_str(&format!(
1004 " Context: {} ({})\n",
1005 error.context.operation, error.context.input
1006 ));
1007
1008 if self.config.include_stack_trace && !error.stack_trace.is_empty() {
1010 output.push_str(" Stack trace:\n");
1011 for frame in &error.stack_trace {
1012 output.push_str(&format!(
1013 " at {} ({}:{})\n",
1014 frame.function,
1015 frame.file.as_ref().unwrap_or(&"unknown".to_string()),
1016 frame.line.unwrap_or(0)
1017 ));
1018 }
1019 }
1020
1021 if self.config.include_suggestions && !error.suggestions.is_empty() {
1023 output.push_str(" Suggestions:\n");
1024 for suggestion in &error.suggestions {
1025 output.push_str(&format!(" - {}\n", suggestion.message));
1026 if let Some(explanation) = &suggestion.explanation {
1027 output.push_str(&format!(" {}\n", explanation));
1028 }
1029 }
1030 }
1031
1032 output.push_str(&self.reset_color());
1033 output
1034 }
1035
1036 fn color_for_severity(&self, severity: &ErrorSeverity) -> &str {
1038 if !self.config.use_colors {
1039 return "";
1040 }
1041
1042 match severity {
1043 ErrorSeverity::Info => "\x1b[36m", ErrorSeverity::Warning => "\x1b[33m", ErrorSeverity::Error => "\x1b[31m", ErrorSeverity::Fatal => "\x1b[35m", ErrorSeverity::Ice => "\x1b[41m", }
1049 }
1050
1051 fn reset_color(&self) -> &str {
1053 if self.config.use_colors {
1054 "\x1b[0m"
1055 } else {
1056 ""
1057 }
1058 }
1059}
1060
1061impl ErrorSeverity {
1062 pub fn as_str(&self) -> &str {
1064 match self {
1065 ErrorSeverity::Info => "INFO",
1066 ErrorSeverity::Warning => "WARNING",
1067 ErrorSeverity::Error => "ERROR",
1068 ErrorSeverity::Fatal => "FATAL",
1069 ErrorSeverity::Ice => "ICE",
1070 }
1071 }
1072}
1073
1074impl ErrorCategory {
1075 pub fn as_str(&self) -> &str {
1077 match self {
1078 ErrorCategory::GraphConstruction => "GRAPH",
1079 ErrorCategory::TypeInference => "TYPE",
1080 ErrorCategory::ShapeInference => "SHAPE",
1081 ErrorCategory::Optimization => "OPT",
1082 ErrorCategory::CodeGeneration => "CODEGEN",
1083 ErrorCategory::Runtime => "RUNTIME",
1084 ErrorCategory::Memory => "MEMORY",
1085 ErrorCategory::Resource => "RESOURCE",
1086 ErrorCategory::UserInput => "INPUT",
1087 ErrorCategory::Internal => "INTERNAL",
1088 ErrorCategory::External => "EXTERNAL",
1089 }
1090 }
1091}
1092
1093impl fmt::Display for DiagnosticError {
1094 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1095 write!(
1096 f,
1097 "[{}] {}: {}",
1098 self.severity.as_str(),
1099 self.category.as_str(),
1100 self.message
1101 )
1102 }
1103}
1104
1105#[cfg(test)]
1106mod tests {
1107 use super::*;
1108
1109 #[test]
1110 fn test_diagnostics_manager_creation() {
1111 let manager = ErrorDiagnosticsManager::with_defaults();
1112 assert!(manager.config.enabled);
1113 assert_eq!(manager.config.max_history_size, 1000);
1114 }
1115
1116 #[test]
1117 fn test_error_recording() {
1118 let mut manager = ErrorDiagnosticsManager::with_defaults();
1119 let error = JitError::RuntimeError("Test error".to_string());
1120
1121 let diagnostic_error = manager.record_error(error);
1122 assert_eq!(diagnostic_error.category, ErrorCategory::Runtime);
1123 assert_eq!(diagnostic_error.severity, ErrorSeverity::Error);
1124 assert!(diagnostic_error.message.contains("Test error"));
1125 }
1126
1127 #[test]
1128 fn test_error_categorization() {
1129 let manager = ErrorDiagnosticsManager::with_defaults();
1130
1131 let graph_error = JitError::GraphError("Graph error".to_string());
1132 assert_eq!(
1133 manager.categorize_error(&graph_error),
1134 ErrorCategory::GraphConstruction
1135 );
1136
1137 let runtime_error = JitError::RuntimeError("Runtime error".to_string());
1138 assert_eq!(
1139 manager.categorize_error(&runtime_error),
1140 ErrorCategory::Runtime
1141 );
1142 }
1143
1144 #[test]
1145 fn test_error_formatting() {
1146 let mut manager = ErrorDiagnosticsManager::with_defaults();
1147 let error = JitError::RuntimeError("Test error".to_string());
1148 let diagnostic_error = manager.record_error(error);
1149
1150 let formatter_config = FormatterConfig::default();
1151 let formatted = manager.format_error(&diagnostic_error, &formatter_config);
1152
1153 assert!(formatted.contains("ERROR"));
1154 assert!(formatted.contains("RUNTIME"));
1155 assert!(formatted.contains("Test error"));
1156 }
1157
1158 #[test]
1159 fn test_context_stack() {
1160 let mut manager = ErrorDiagnosticsManager::with_defaults();
1161
1162 let context = DiagnosticContext {
1163 operation: "test_operation".to_string(),
1164 input: "test_input".to_string(),
1165 expected: None,
1166 actual: None,
1167 environment: manager.get_environment_info(),
1168 data: HashMap::new(),
1169 };
1170
1171 manager.push_context(context.clone());
1172 assert_eq!(manager.context_stack.len(), 1);
1173
1174 let popped = manager.pop_context().unwrap();
1175 assert_eq!(popped.operation, "test_operation");
1176 assert_eq!(manager.context_stack.len(), 0);
1177 }
1178}