blob: b26928648680e109bae06a86b23fe72b0893ddac [file] [log] [blame]
David Tolnay0d85ccd2020-08-30 20:18:13 -07001use crate::gen::fs;
David Tolnay7db73692019-10-20 14:51:12 -04002use crate::syntax;
David Tolnay7db73692019-10-20 14:51:12 -04003use codespan_reporting::diagnostic::{Diagnostic, Label};
David Tolnaye2e7bc32020-03-18 14:02:01 -07004use codespan_reporting::files::SimpleFiles;
David Tolnay7db73692019-10-20 14:51:12 -04005use codespan_reporting::term::termcolor::{ColorChoice, StandardStream, WriteColor};
6use codespan_reporting::term::{self, Config};
David Tolnay52509842020-04-25 19:38:59 -07007use std::error::Error as StdError;
8use std::fmt::{self, Display};
9use std::io::{self, Write};
David Tolnay7db73692019-10-20 14:51:12 -040010use std::ops::Range;
David Tolnaydbff3c42020-08-31 00:41:53 -070011use std::path::{Path, PathBuf};
David Tolnay7db73692019-10-20 14:51:12 -040012use std::process;
David Tolnaydbff3c42020-08-31 00:41:53 -070013use std::str::Utf8Error;
David Tolnay7db73692019-10-20 14:51:12 -040014
David Tolnay6f7f6862020-08-29 22:55:03 -070015pub(crate) type Result<T, E = Error> = std::result::Result<T, E>;
David Tolnay52509842020-04-25 19:38:59 -070016
17#[derive(Debug)]
David Tolnaye9c533e2020-08-29 23:03:51 -070018pub(crate) enum Error {
David Tolnay52509842020-04-25 19:38:59 -070019 NoBridgeMod,
David Tolnay0d85ccd2020-08-30 20:18:13 -070020 Fs(fs::Error),
David Tolnaydbff3c42020-08-31 00:41:53 -070021 Utf8(PathBuf, Utf8Error),
David Tolnay52509842020-04-25 19:38:59 -070022 Syn(syn::Error),
23}
24
25impl Display for Error {
26 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
27 match self {
28 Error::NoBridgeMod => write!(f, "no #[cxx::bridge] module found"),
David Tolnay0d85ccd2020-08-30 20:18:13 -070029 Error::Fs(err) => err.fmt(f),
David Tolnaydbff3c42020-08-31 00:41:53 -070030 Error::Utf8(path, _) => write!(f, "Failed to read file `{}`", path.display()),
David Tolnay52509842020-04-25 19:38:59 -070031 Error::Syn(err) => err.fmt(f),
32 }
33 }
34}
35
36impl StdError for Error {
37 fn source(&self) -> Option<&(dyn StdError + 'static)> {
38 match self {
David Tolnay0d85ccd2020-08-30 20:18:13 -070039 Error::Fs(err) => err.source(),
David Tolnaydbff3c42020-08-31 00:41:53 -070040 Error::Utf8(_, err) => Some(err),
David Tolnayb87d70f2020-08-30 20:02:06 -070041 Error::Syn(err) => err.source(),
David Tolnay52509842020-04-25 19:38:59 -070042 _ => None,
43 }
44 }
45}
46
David Tolnay0d85ccd2020-08-30 20:18:13 -070047impl From<fs::Error> for Error {
48 fn from(err: fs::Error) -> Self {
49 Error::Fs(err)
David Tolnay52509842020-04-25 19:38:59 -070050 }
51}
52
53impl From<syn::Error> for Error {
54 fn from(err: syn::Error) -> Self {
55 Error::Syn(err)
56 }
57}
58
David Tolnay7db73692019-10-20 14:51:12 -040059pub(super) fn format_err(path: &Path, source: &str, error: Error) -> ! {
60 match error {
61 Error::Syn(syn_error) => {
David Tolnay1ba09e12020-05-01 10:06:37 -070062 let syn_error = sort_syn_errors(syn_error);
David Tolnay7db73692019-10-20 14:51:12 -040063 let writer = StandardStream::stderr(ColorChoice::Auto);
64 let ref mut stderr = writer.lock();
65 for error in syn_error {
66 let _ = writeln!(stderr);
67 display_syn_error(stderr, path, source, error);
68 }
69 }
David Tolnaybb3ba502020-08-30 19:59:41 -070070 _ => {
71 let _ = writeln!(io::stderr(), "cxxbridge: {}", report(error));
72 }
David Tolnay7db73692019-10-20 14:51:12 -040073 }
74 process::exit(1);
75}
76
David Tolnaybb3ba502020-08-30 19:59:41 -070077pub(crate) fn report(error: impl StdError) -> impl Display {
78 struct Report<E>(E);
79
80 impl<E: StdError> Display for Report<E> {
81 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
82 Display::fmt(&self.0, formatter)?;
83 let mut error: &dyn StdError = &self.0;
84
85 while let Some(cause) = error.source() {
86 formatter.write_str("\n\nCaused by:\n ")?;
87 Display::fmt(cause, formatter)?;
88 error = cause;
89 }
90
91 Ok(())
92 }
93 }
94
95 Report(error)
96}
97
David Tolnay1ba09e12020-05-01 10:06:37 -070098fn sort_syn_errors(error: syn::Error) -> Vec<syn::Error> {
99 let mut errors: Vec<_> = error.into_iter().collect();
100 errors.sort_by_key(|e| {
101 let start = e.span().start();
102 (start.line, start.column)
103 });
104 errors
105}
106
David Tolnay7db73692019-10-20 14:51:12 -0400107fn display_syn_error(stderr: &mut dyn WriteColor, path: &Path, source: &str, error: syn::Error) {
108 let span = error.span();
109 let start = span.start();
110 let end = span.end();
111
112 let mut start_offset = 0;
113 for _ in 1..start.line {
114 start_offset += source[start_offset..].find('\n').unwrap() + 1;
115 }
David Tolnayef8fd192020-05-22 01:17:52 -0700116 let start_column = source[start_offset..]
117 .chars()
118 .take(start.column)
119 .map(char::len_utf8)
120 .sum::<usize>();
121 start_offset += start_column;
David Tolnay7db73692019-10-20 14:51:12 -0400122
123 let mut end_offset = start_offset;
124 if start.line == end.line {
David Tolnayef8fd192020-05-22 01:17:52 -0700125 end_offset -= start_column;
David Tolnay7db73692019-10-20 14:51:12 -0400126 } else {
127 for _ in 0..end.line - start.line {
128 end_offset += source[end_offset..].find('\n').unwrap() + 1;
129 }
130 }
David Tolnayef8fd192020-05-22 01:17:52 -0700131 end_offset += source[end_offset..]
132 .chars()
133 .take(end.column)
134 .map(char::len_utf8)
135 .sum::<usize>();
David Tolnay7db73692019-10-20 14:51:12 -0400136
David Tolnaye2e7bc32020-03-18 14:02:01 -0700137 let mut files = SimpleFiles::new();
David Tolnay7db73692019-10-20 14:51:12 -0400138 let file = files.add(path.to_string_lossy(), source);
139
David Tolnaye2e7bc32020-03-18 14:02:01 -0700140 let diagnostic = diagnose(file, start_offset..end_offset, error);
David Tolnay7db73692019-10-20 14:51:12 -0400141
142 let config = Config::default();
143 let _ = term::emit(stderr, &config, &files, &diagnostic);
144}
145
David Tolnaye2e7bc32020-03-18 14:02:01 -0700146fn diagnose(file: usize, range: Range<usize>, error: syn::Error) -> Diagnostic<usize> {
David Tolnay7db73692019-10-20 14:51:12 -0400147 let message = error.to_string();
148 let info = syntax::error::ERRORS
149 .iter()
150 .find(|e| message.contains(e.msg));
David Tolnaye2e7bc32020-03-18 14:02:01 -0700151 let mut diagnostic = Diagnostic::error().with_message(&message);
152 let mut label = Label::primary(file, range);
153 if let Some(info) = info {
154 label.message = info.label.map_or(message, str::to_owned);
155 diagnostic.labels.push(label);
156 diagnostic.notes.extend(info.note.map(str::to_owned));
David Tolnay7db73692019-10-20 14:51:12 -0400157 } else {
David Tolnaye2e7bc32020-03-18 14:02:01 -0700158 label.message = message;
159 diagnostic.labels.push(label);
160 }
David Tolnay7db73692019-10-20 14:51:12 -0400161 diagnostic.code = Some("cxxbridge".to_owned());
162 diagnostic
163}