blob: b8ac337fc5888412779fa9b6618f16a36779bfbb [file] [log] [blame]
David Tolnay7db73692019-10-20 14:51:12 -04001use crate::gen::Error;
2use crate::syntax;
3use anyhow::anyhow;
4use codespan::{FileId, Files};
5use codespan_reporting::diagnostic::{Diagnostic, Label};
6use codespan_reporting::term::termcolor::{ColorChoice, StandardStream, WriteColor};
7use codespan_reporting::term::{self, Config};
8use std::io::Write;
9use std::ops::Range;
10use std::path::Path;
11use std::process;
12
13pub(super) fn format_err(path: &Path, source: &str, error: Error) -> ! {
14 match error {
15 Error::Syn(syn_error) => {
16 let writer = StandardStream::stderr(ColorChoice::Auto);
17 let ref mut stderr = writer.lock();
18 for error in syn_error {
19 let _ = writeln!(stderr);
20 display_syn_error(stderr, path, source, error);
21 }
22 }
23 _ => eprintln!("cxxbridge: {:?}", anyhow!(error)),
24 }
25 process::exit(1);
26}
27
28fn display_syn_error(stderr: &mut dyn WriteColor, path: &Path, source: &str, error: syn::Error) {
29 let span = error.span();
30 let start = span.start();
31 let end = span.end();
32
33 let mut start_offset = 0;
34 for _ in 1..start.line {
35 start_offset += source[start_offset..].find('\n').unwrap() + 1;
36 }
37 start_offset += start.column;
38
39 let mut end_offset = start_offset;
40 if start.line == end.line {
41 end_offset -= start.column;
42 } else {
43 for _ in 0..end.line - start.line {
44 end_offset += source[end_offset..].find('\n').unwrap() + 1;
45 }
46 }
47 end_offset += end.column;
48
49 let mut files = Files::new();
50 let file = files.add(path.to_string_lossy(), source);
51
52 let range = start_offset as u32..end_offset as u32;
53 let diagnostic = diagnose(file, range, error);
54
55 let config = Config::default();
56 let _ = term::emit(stderr, &config, &files, &diagnostic);
57}
58
59fn diagnose(file: FileId, range: Range<u32>, error: syn::Error) -> Diagnostic {
60 let message = error.to_string();
61 let info = syntax::error::ERRORS
62 .iter()
63 .find(|e| message.contains(e.msg));
64 let mut diagnostic = if let Some(info) = info {
65 let label = Label::new(file, range, info.label.unwrap_or(&message));
66 let mut diagnostic = Diagnostic::new_error(&message, label);
67 if let Some(note) = info.note {
68 diagnostic = diagnostic.with_notes(vec![note.to_owned()]);
69 }
70 diagnostic
71 } else {
72 let label = Label::new(file, range, &message);
73 Diagnostic::new_error(&message, label)
74 };
75 diagnostic.code = Some("cxxbridge".to_owned());
76 diagnostic
77}