blob: b91c166ba7902c3398a98b8654c31ee842b69486 [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
David Tolnay52509842020-04-25 19:38:59 -070014pub(super) type Result<T, E = Error> = std::result::Result<T, E>;
15
16#[derive(Debug)]
17pub(super) enum Error {
18 NoBridgeMod,
19 OutOfLineMod,
20 Io(io::Error),
21 Syn(syn::Error),
22}
23
24impl Display for Error {
25 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
26 match self {
27 Error::NoBridgeMod => write!(f, "no #[cxx::bridge] module found"),
28 Error::OutOfLineMod => write!(f, "#[cxx::bridge] module must have inline contents"),
29 Error::Io(err) => err.fmt(f),
30 Error::Syn(err) => err.fmt(f),
31 }
32 }
33}
34
35impl StdError for Error {
36 fn source(&self) -> Option<&(dyn StdError + 'static)> {
37 match self {
38 Error::Io(err) => Some(err),
39 Error::Syn(err) => Some(err),
40 _ => None,
41 }
42 }
43}
44
45impl From<io::Error> for Error {
46 fn from(err: io::Error) -> Self {
47 Error::Io(err)
48 }
49}
50
51impl From<syn::Error> for Error {
52 fn from(err: syn::Error) -> Self {
53 Error::Syn(err)
54 }
55}
56
David Tolnay7db73692019-10-20 14:51:12 -040057pub(super) fn format_err(path: &Path, source: &str, error: Error) -> ! {
58 match error {
59 Error::Syn(syn_error) => {
David Tolnay1ba09e12020-05-01 10:06:37 -070060 let syn_error = sort_syn_errors(syn_error);
David Tolnay7db73692019-10-20 14:51:12 -040061 let writer = StandardStream::stderr(ColorChoice::Auto);
62 let ref mut stderr = writer.lock();
63 for error in syn_error {
64 let _ = writeln!(stderr);
65 display_syn_error(stderr, path, source, error);
66 }
67 }
68 _ => eprintln!("cxxbridge: {:?}", anyhow!(error)),
69 }
70 process::exit(1);
71}
72
David Tolnay1ba09e12020-05-01 10:06:37 -070073fn sort_syn_errors(error: syn::Error) -> Vec<syn::Error> {
74 let mut errors: Vec<_> = error.into_iter().collect();
75 errors.sort_by_key(|e| {
76 let start = e.span().start();
77 (start.line, start.column)
78 });
79 errors
80}
81
David Tolnay7db73692019-10-20 14:51:12 -040082fn display_syn_error(stderr: &mut dyn WriteColor, path: &Path, source: &str, error: syn::Error) {
83 let span = error.span();
84 let start = span.start();
85 let end = span.end();
86
87 let mut start_offset = 0;
88 for _ in 1..start.line {
89 start_offset += source[start_offset..].find('\n').unwrap() + 1;
90 }
91 start_offset += start.column;
92
93 let mut end_offset = start_offset;
94 if start.line == end.line {
95 end_offset -= start.column;
96 } else {
97 for _ in 0..end.line - start.line {
98 end_offset += source[end_offset..].find('\n').unwrap() + 1;
99 }
100 }
101 end_offset += end.column;
102
David Tolnaye2e7bc32020-03-18 14:02:01 -0700103 let mut files = SimpleFiles::new();
David Tolnay7db73692019-10-20 14:51:12 -0400104 let file = files.add(path.to_string_lossy(), source);
105
David Tolnaye2e7bc32020-03-18 14:02:01 -0700106 let diagnostic = diagnose(file, start_offset..end_offset, error);
David Tolnay7db73692019-10-20 14:51:12 -0400107
108 let config = Config::default();
109 let _ = term::emit(stderr, &config, &files, &diagnostic);
110}
111
David Tolnaye2e7bc32020-03-18 14:02:01 -0700112fn diagnose(file: usize, range: Range<usize>, error: syn::Error) -> Diagnostic<usize> {
David Tolnay7db73692019-10-20 14:51:12 -0400113 let message = error.to_string();
114 let info = syntax::error::ERRORS
115 .iter()
116 .find(|e| message.contains(e.msg));
David Tolnaye2e7bc32020-03-18 14:02:01 -0700117 let mut diagnostic = Diagnostic::error().with_message(&message);
118 let mut label = Label::primary(file, range);
119 if let Some(info) = info {
120 label.message = info.label.map_or(message, str::to_owned);
121 diagnostic.labels.push(label);
122 diagnostic.notes.extend(info.note.map(str::to_owned));
David Tolnay7db73692019-10-20 14:51:12 -0400123 } else {
David Tolnaye2e7bc32020-03-18 14:02:01 -0700124 label.message = message;
125 diagnostic.labels.push(label);
126 }
David Tolnay7db73692019-10-20 14:51:12 -0400127 diagnostic.code = Some("cxxbridge".to_owned());
128 diagnostic
129}