blob: 61afd4dd9acaf06df93e1fbf1bfbcaa491d725ab [file] [log] [blame]
David Tolnay7db73692019-10-20 14:51:12 -04001use crate::syntax;
2use anyhow::anyhow;
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;
11use std::path::Path;
12use std::process;
13
Adrian Taylor9fc08462020-08-14 10:51:00 -070014pub type Result<T, E = Error> = std::result::Result<T, E>;
David Tolnay52509842020-04-25 19:38:59 -070015
16#[derive(Debug)]
Adrian Taylor9fc08462020-08-14 10:51:00 -070017pub enum Error {
18 /// No `#[cxx::bridge]` module could be found.
David Tolnay52509842020-04-25 19:38:59 -070019 NoBridgeMod,
Adrian Taylor9fc08462020-08-14 10:51:00 -070020 /// `#[cxx::bridge]` was attached to something other than
21 /// an inline module.
David Tolnay52509842020-04-25 19:38:59 -070022 OutOfLineMod,
Adrian Taylor9fc08462020-08-14 10:51:00 -070023 /// An IO error occurred when reading Rust code.
David Tolnay52509842020-04-25 19:38:59 -070024 Io(io::Error),
Adrian Taylor9fc08462020-08-14 10:51:00 -070025 /// A syntax error occurred when parsing Rust code.
David Tolnay52509842020-04-25 19:38:59 -070026 Syn(syn::Error),
27}
28
29impl Display for Error {
30 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
31 match self {
32 Error::NoBridgeMod => write!(f, "no #[cxx::bridge] module found"),
33 Error::OutOfLineMod => write!(f, "#[cxx::bridge] module must have inline contents"),
34 Error::Io(err) => err.fmt(f),
35 Error::Syn(err) => err.fmt(f),
36 }
37 }
38}
39
40impl StdError for Error {
41 fn source(&self) -> Option<&(dyn StdError + 'static)> {
42 match self {
43 Error::Io(err) => Some(err),
44 Error::Syn(err) => Some(err),
45 _ => None,
46 }
47 }
48}
49
50impl From<io::Error> for Error {
51 fn from(err: io::Error) -> Self {
52 Error::Io(err)
53 }
54}
55
56impl From<syn::Error> for Error {
57 fn from(err: syn::Error) -> Self {
58 Error::Syn(err)
59 }
60}
61
David Tolnay7db73692019-10-20 14:51:12 -040062pub(super) fn format_err(path: &Path, source: &str, error: Error) -> ! {
63 match error {
64 Error::Syn(syn_error) => {
David Tolnay1ba09e12020-05-01 10:06:37 -070065 let syn_error = sort_syn_errors(syn_error);
David Tolnay7db73692019-10-20 14:51:12 -040066 let writer = StandardStream::stderr(ColorChoice::Auto);
67 let ref mut stderr = writer.lock();
68 for error in syn_error {
69 let _ = writeln!(stderr);
70 display_syn_error(stderr, path, source, error);
71 }
72 }
73 _ => eprintln!("cxxbridge: {:?}", anyhow!(error)),
74 }
75 process::exit(1);
76}
77
David Tolnay1ba09e12020-05-01 10:06:37 -070078fn sort_syn_errors(error: syn::Error) -> Vec<syn::Error> {
79 let mut errors: Vec<_> = error.into_iter().collect();
80 errors.sort_by_key(|e| {
81 let start = e.span().start();
82 (start.line, start.column)
83 });
84 errors
85}
86
David Tolnay7db73692019-10-20 14:51:12 -040087fn display_syn_error(stderr: &mut dyn WriteColor, path: &Path, source: &str, error: syn::Error) {
88 let span = error.span();
89 let start = span.start();
90 let end = span.end();
91
92 let mut start_offset = 0;
93 for _ in 1..start.line {
94 start_offset += source[start_offset..].find('\n').unwrap() + 1;
95 }
David Tolnayef8fd192020-05-22 01:17:52 -070096 let start_column = source[start_offset..]
97 .chars()
98 .take(start.column)
99 .map(char::len_utf8)
100 .sum::<usize>();
101 start_offset += start_column;
David Tolnay7db73692019-10-20 14:51:12 -0400102
103 let mut end_offset = start_offset;
104 if start.line == end.line {
David Tolnayef8fd192020-05-22 01:17:52 -0700105 end_offset -= start_column;
David Tolnay7db73692019-10-20 14:51:12 -0400106 } else {
107 for _ in 0..end.line - start.line {
108 end_offset += source[end_offset..].find('\n').unwrap() + 1;
109 }
110 }
David Tolnayef8fd192020-05-22 01:17:52 -0700111 end_offset += source[end_offset..]
112 .chars()
113 .take(end.column)
114 .map(char::len_utf8)
115 .sum::<usize>();
David Tolnay7db73692019-10-20 14:51:12 -0400116
David Tolnaye2e7bc32020-03-18 14:02:01 -0700117 let mut files = SimpleFiles::new();
David Tolnay7db73692019-10-20 14:51:12 -0400118 let file = files.add(path.to_string_lossy(), source);
119
David Tolnaye2e7bc32020-03-18 14:02:01 -0700120 let diagnostic = diagnose(file, start_offset..end_offset, error);
David Tolnay7db73692019-10-20 14:51:12 -0400121
122 let config = Config::default();
123 let _ = term::emit(stderr, &config, &files, &diagnostic);
124}
125
David Tolnaye2e7bc32020-03-18 14:02:01 -0700126fn diagnose(file: usize, range: Range<usize>, error: syn::Error) -> Diagnostic<usize> {
David Tolnay7db73692019-10-20 14:51:12 -0400127 let message = error.to_string();
128 let info = syntax::error::ERRORS
129 .iter()
130 .find(|e| message.contains(e.msg));
David Tolnaye2e7bc32020-03-18 14:02:01 -0700131 let mut diagnostic = Diagnostic::error().with_message(&message);
132 let mut label = Label::primary(file, range);
133 if let Some(info) = info {
134 label.message = info.label.map_or(message, str::to_owned);
135 diagnostic.labels.push(label);
136 diagnostic.notes.extend(info.note.map(str::to_owned));
David Tolnay7db73692019-10-20 14:51:12 -0400137 } else {
David Tolnaye2e7bc32020-03-18 14:02:01 -0700138 label.message = message;
139 diagnostic.labels.push(label);
140 }
David Tolnay7db73692019-10-20 14:51:12 -0400141 diagnostic.code = Some("cxxbridge".to_owned());
142 diagnostic
143}