oxur_comp/
error_translator.rs1use crate::RustcDiagnostic;
34use oxur_smap::SourceMap;
35
36pub struct ErrorTranslator {
38 source_map: SourceMap,
39}
40
41impl ErrorTranslator {
42 pub fn new(source_map: SourceMap) -> Self {
44 Self { source_map }
45 }
46
47 pub fn translate_diagnostic(&self, diagnostic: &RustcDiagnostic) -> String {
52 let mut output = String::new();
53
54 if let Some((rust_file, rust_line, rust_col)) = diagnostic.primary_position() {
56 output.push_str(&format!("error: {}\n", diagnostic.message));
60
61 output.push_str(&format!(" --> {}:{}:{}\n", rust_file, rust_line, rust_col));
63
64 output.push_str(" (Note: Error position translation not yet implemented)\n");
66 } else {
67 output.push_str(&format!("error: {}\n", diagnostic.message));
69 }
70
71 if let Some(code) = &diagnostic.code {
73 output.push_str(&format!(" code: {}\n", code.code));
74 }
75
76 output
77 }
78
79 pub fn translate_diagnostics(&self, diagnostics: &[RustcDiagnostic]) -> String {
81 diagnostics
82 .iter()
83 .filter(|d| d.is_error())
84 .map(|d| self.translate_diagnostic(d))
85 .collect::<Vec<_>>()
86 .join("\n")
87 }
88
89 pub fn source_map(&self) -> &SourceMap {
91 &self.source_map
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98 use crate::RustcDiagnostic;
99
100 #[test]
101 fn test_translator_creation() {
102 let source_map = oxur_smap::SourceMap::new();
103 let translator = ErrorTranslator::new(source_map);
104 assert_eq!(translator.source_map().stats().surface_nodes, 0);
105 }
106
107 #[test]
108 fn test_translate_diagnostic_with_position() {
109 let source_map = oxur_smap::SourceMap::new();
110 let translator = ErrorTranslator::new(source_map);
111
112 let json = r#"{
113 "message": "cannot find value `x` in this scope",
114 "code": {
115 "code": "E0425",
116 "explanation": null
117 },
118 "level": "error",
119 "spans": [
120 {
121 "file_name": "generated.rs",
122 "byte_start": 42,
123 "byte_end": 43,
124 "line_start": 5,
125 "line_end": 5,
126 "column_start": 10,
127 "column_end": 11,
128 "is_primary": true,
129 "text": [],
130 "label": "not found in this scope",
131 "suggested_replacement": null,
132 "suggestion_applicability": null,
133 "expansion": null
134 }
135 ],
136 "children": [],
137 "rendered": null
138 }"#;
139
140 let diagnostic = RustcDiagnostic::from_json(json).unwrap();
141 let output = translator.translate_diagnostic(&diagnostic);
142
143 assert!(output.contains("cannot find value `x`"));
145
146 assert!(output.contains("generated.rs:5:10"));
148
149 assert!(output.contains("E0425"));
151
152 assert!(output.contains("translation not yet implemented"));
154 }
155
156 #[test]
157 fn test_translate_diagnostic_without_position() {
158 let source_map = oxur_smap::SourceMap::new();
159 let translator = ErrorTranslator::new(source_map);
160
161 let json = r#"{
162 "message": "aborting due to previous error",
163 "code": null,
164 "level": "error",
165 "spans": [],
166 "children": [],
167 "rendered": null
168 }"#;
169
170 let diagnostic = RustcDiagnostic::from_json(json).unwrap();
171 let output = translator.translate_diagnostic(&diagnostic);
172
173 assert!(output.contains("aborting due to previous error"));
175
176 assert!(!output.contains("-->"));
178 }
179
180 #[test]
181 fn test_translate_multiple_diagnostics() {
182 let source_map = oxur_smap::SourceMap::new();
183 let translator = ErrorTranslator::new(source_map);
184
185 let json_lines = r#"{"message": "error 1", "code": null, "level": "error", "spans": [], "children": [], "rendered": null}
186{"message": "warning 1", "code": null, "level": "warning", "spans": [], "children": [], "rendered": null}
187{"message": "error 2", "code": null, "level": "error", "spans": [], "children": [], "rendered": null}"#;
188
189 let diagnostics = RustcDiagnostic::from_json_lines(json_lines).unwrap();
190 let output = translator.translate_diagnostics(&diagnostics);
191
192 assert!(output.contains("error 1"));
194 assert!(output.contains("error 2"));
195 assert!(!output.contains("warning 1"));
196 }
197
198 #[test]
199 fn test_translator_with_populated_source_map() {
200 use oxur_lang::{Expander, Parser};
201
202 let source = r#"(deffn main ()
204 (println! "Hello"))"#;
205
206 let mut parser = Parser::new(source.to_string());
207 let surface_forms = parser.parse().unwrap();
208
209 let mut expander = Expander::new();
210 let _core_forms = expander.expand(surface_forms).unwrap();
211 let source_map = expander.source_map().clone();
212
213 let stats = source_map.stats();
215 assert!(stats.surface_nodes > 0, "Should have surface mappings");
216
217 let translator = ErrorTranslator::new(source_map);
218
219 assert!(translator.source_map().stats().surface_nodes > 0);
221 }
222
223 #[test]
224 fn test_source_map_accessor() {
225 let mut source_map = oxur_smap::SourceMap::new();
226 let node_id = oxur_smap::new_node_id();
227 let pos = oxur_smap::SourcePos::new("test.oxur".to_string(), 1, 1, 1);
228 source_map.record_surface_node(node_id, pos);
229
230 let translator = ErrorTranslator::new(source_map);
231
232 let retrieved_pos = translator.source_map().get_surface_position(&node_id);
234 assert!(retrieved_pos.is_some());
235 }
236}