blob: a7ce6e14f67eb8a7c1fb503dc97ca313573a9eef [file] [log] [blame]
David Tolnay7db73692019-10-20 14:51:12 -04001use crate::syntax;
David Tolnay7db73692019-10-20 14:51:12 -04002use codespan_reporting::diagnostic::{Diagnostic, Label};
David Tolnaye2e7bc32020-03-18 14:02:01 -07003use codespan_reporting::files::SimpleFiles;
David Tolnay7db73692019-10-20 14:51:12 -04004use codespan_reporting::term::termcolor::{ColorChoice, StandardStream, WriteColor};
5use codespan_reporting::term::{self, Config};
David Tolnay52509842020-04-25 19:38:59 -07006use std::error::Error as StdError;
7use std::fmt::{self, Display};
8use std::io::{self, Write};
David Tolnay7db73692019-10-20 14:51:12 -04009use std::ops::Range;
10use std::path::Path;
11use std::process;
12
David Tolnay6f7f6862020-08-29 22:55:03 -070013pub(crate) type Result<T, E = Error> = std::result::Result<T, E>;
David Tolnay52509842020-04-25 19:38:59 -070014
15#[derive(Debug)]
David Tolnaye9c533e2020-08-29 23:03:51 -070016pub(crate) enum Error {
David Tolnay52509842020-04-25 19:38:59 -070017 NoBridgeMod,
David Tolnay52509842020-04-25 19:38:59 -070018 Io(io::Error),
19 Syn(syn::Error),
20}
21
22impl Display for Error {
23 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
24 match self {
25 Error::NoBridgeMod => write!(f, "no #[cxx::bridge] module found"),
David Tolnay52509842020-04-25 19:38:59 -070026 Error::Io(err) => err.fmt(f),
27 Error::Syn(err) => err.fmt(f),
28 }
29 }
30}
31
32impl StdError for Error {
33 fn source(&self) -> Option<&(dyn StdError + 'static)> {
34 match self {
David Tolnayb87d70f2020-08-30 20:02:06 -070035 Error::Io(err) => err.source(),
36 Error::Syn(err) => err.source(),
David Tolnay52509842020-04-25 19:38:59 -070037 _ => None,
38 }
39 }
40}
41
42impl From<io::Error> for Error {
43 fn from(err: io::Error) -> Self {
44 Error::Io(err)
45 }
46}
47
48impl From<syn::Error> for Error {
49 fn from(err: syn::Error) -> Self {
50 Error::Syn(err)
51 }
52}
53
David Tolnay7db73692019-10-20 14:51:12 -040054pub(super) fn format_err(path: &Path, source: &str, error: Error) -> ! {
55 match error {
56 Error::Syn(syn_error) => {
David Tolnay1ba09e12020-05-01 10:06:37 -070057 let syn_error = sort_syn_errors(syn_error);
David Tolnay7db73692019-10-20 14:51:12 -040058 let writer = StandardStream::stderr(ColorChoice::Auto);
59 let ref mut stderr = writer.lock();
60 for error in syn_error {
61 let _ = writeln!(stderr);
62 display_syn_error(stderr, path, source, error);
63 }
64 }
David Tolnaybb3ba502020-08-30 19:59:41 -070065 _ => {
66 let _ = writeln!(io::stderr(), "cxxbridge: {}", report(error));
67 }
David Tolnay7db73692019-10-20 14:51:12 -040068 }
69 process::exit(1);
70}
71
David Tolnaybb3ba502020-08-30 19:59:41 -070072pub(crate) fn report(error: impl StdError) -> impl Display {
73 struct Report<E>(E);
74
75 impl<E: StdError> Display for Report<E> {
76 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
77 Display::fmt(&self.0, formatter)?;
78 let mut error: &dyn StdError = &self.0;
79
80 while let Some(cause) = error.source() {
81 formatter.write_str("\n\nCaused by:\n ")?;
82 Display::fmt(cause, formatter)?;
83 error = cause;
84 }
85
86 Ok(())
87 }
88 }
89
90 Report(error)
91}
92
David Tolnay1ba09e12020-05-01 10:06:37 -070093fn sort_syn_errors(error: syn::Error) -> Vec<syn::Error> {
94 let mut errors: Vec<_> = error.into_iter().collect();
95 errors.sort_by_key(|e| {
96 let start = e.span().start();
97 (start.line, start.column)
98 });
99 errors
100}
101
David Tolnay7db73692019-10-20 14:51:12 -0400102fn display_syn_error(stderr: &mut dyn WriteColor, path: &Path, source: &str, error: syn::Error) {
103 let span = error.span();
104 let start = span.start();
105 let end = span.end();
106
107 let mut start_offset = 0;
108 for _ in 1..start.line {
109 start_offset += source[start_offset..].find('\n').unwrap() + 1;
110 }
David Tolnayef8fd192020-05-22 01:17:52 -0700111 let start_column = source[start_offset..]
112 .chars()
113 .take(start.column)
114 .map(char::len_utf8)
115 .sum::<usize>();
116 start_offset += start_column;
David Tolnay7db73692019-10-20 14:51:12 -0400117
118 let mut end_offset = start_offset;
119 if start.line == end.line {
David Tolnayef8fd192020-05-22 01:17:52 -0700120 end_offset -= start_column;
David Tolnay7db73692019-10-20 14:51:12 -0400121 } else {
122 for _ in 0..end.line - start.line {
123 end_offset += source[end_offset..].find('\n').unwrap() + 1;
124 }
125 }
David Tolnayef8fd192020-05-22 01:17:52 -0700126 end_offset += source[end_offset..]
127 .chars()
128 .take(end.column)
129 .map(char::len_utf8)
130 .sum::<usize>();
David Tolnay7db73692019-10-20 14:51:12 -0400131
David Tolnaye2e7bc32020-03-18 14:02:01 -0700132 let mut files = SimpleFiles::new();
David Tolnay7db73692019-10-20 14:51:12 -0400133 let file = files.add(path.to_string_lossy(), source);
134
David Tolnaye2e7bc32020-03-18 14:02:01 -0700135 let diagnostic = diagnose(file, start_offset..end_offset, error);
David Tolnay7db73692019-10-20 14:51:12 -0400136
137 let config = Config::default();
138 let _ = term::emit(stderr, &config, &files, &diagnostic);
139}
140
David Tolnaye2e7bc32020-03-18 14:02:01 -0700141fn diagnose(file: usize, range: Range<usize>, error: syn::Error) -> Diagnostic<usize> {
David Tolnay7db73692019-10-20 14:51:12 -0400142 let message = error.to_string();
143 let info = syntax::error::ERRORS
144 .iter()
145 .find(|e| message.contains(e.msg));
David Tolnaye2e7bc32020-03-18 14:02:01 -0700146 let mut diagnostic = Diagnostic::error().with_message(&message);
147 let mut label = Label::primary(file, range);
148 if let Some(info) = info {
149 label.message = info.label.map_or(message, str::to_owned);
150 diagnostic.labels.push(label);
151 diagnostic.notes.extend(info.note.map(str::to_owned));
David Tolnay7db73692019-10-20 14:51:12 -0400152 } else {
David Tolnaye2e7bc32020-03-18 14:02:01 -0700153 label.message = message;
154 diagnostic.labels.push(label);
155 }
David Tolnay7db73692019-10-20 14:51:12 -0400156 diagnostic.code = Some("cxxbridge".to_owned());
157 diagnostic
158}