cxx_build/gen/
error.rs

1use crate::gen::fs;
2use crate::syntax;
3use codespan_reporting::diagnostic::{Diagnostic, Label};
4use codespan_reporting::files::SimpleFiles;
5use codespan_reporting::term::termcolor::{ColorChoice, StandardStream, WriteColor};
6use codespan_reporting::term::{self, Config};
7use std::borrow::Cow;
8use std::error::Error as StdError;
9use std::fmt::{self, Display};
10use std::io::{self, Write};
11use std::ops::Range;
12use std::path::{Path, PathBuf};
13use std::process;
14use std::str::Utf8Error;
15
16pub(crate) type Result<T, E = Error> = std::result::Result<T, E>;
17
18#[derive(#[automatically_derived]
impl ::core::fmt::Debug for Error {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            Error::NoBridgeMod =>
                ::core::fmt::Formatter::write_str(f, "NoBridgeMod"),
            Error::Fs(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Fs",
                    &__self_0),
            Error::Utf8(__self_0, __self_1) =>
                ::core::fmt::Formatter::debug_tuple_field2_finish(f, "Utf8",
                    __self_0, &__self_1),
            Error::Syn(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Syn",
                    &__self_0),
        }
    }
}Debug)]
19pub(crate) enum Error {
20    NoBridgeMod,
21    Fs(fs::Error),
22    Utf8(PathBuf, Utf8Error),
23    Syn(syn::Error),
24}
25
26impl Display for Error {
27    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
28        match self {
29            Error::NoBridgeMod => f.write_fmt(format_args!("no #[cxx::bridge] module found"))write!(f, "no #[cxx::bridge] module found"),
30            Error::Fs(err) => err.fmt(f),
31            Error::Utf8(path, _) => f.write_fmt(format_args!("Failed to read file `{0}`", path.display()))write!(f, "Failed to read file `{}`", path.display()),
32            Error::Syn(err) => err.fmt(f),
33        }
34    }
35}
36
37impl StdError for Error {
38    fn source(&self) -> Option<&(dyn StdError + 'static)> {
39        match self {
40            Error::Fs(err) => err.source(),
41            Error::Utf8(_, err) => Some(err),
42            Error::Syn(err) => err.source(),
43            Error::NoBridgeMod => None,
44        }
45    }
46}
47
48impl From<fs::Error> for Error {
49    fn from(err: fs::Error) -> Self {
50        Error::Fs(err)
51    }
52}
53
54impl From<syn::Error> for Error {
55    fn from(err: syn::Error) -> Self {
56        Error::Syn(err)
57    }
58}
59
60pub(super) fn format_err(path: &Path, source: &str, error: Error) -> ! {
61    match error {
62        Error::Syn(syn_error) => {
63            let syn_error = sort_syn_errors(syn_error);
64            let writer = StandardStream::stderr(ColorChoice::Auto);
65            let ref mut stderr = writer.lock();
66            for error in syn_error {
67                let _ = stderr.write_fmt(format_args!("\n"))writeln!(stderr);
68                display_syn_error(stderr, path, source, error);
69            }
70        }
71        Error::NoBridgeMod => {
72            let _ = io::stderr().write_fmt(format_args!("cxxbridge: no #[cxx::bridge] module found in {0}\n",
        path.display()))writeln!(
73                io::stderr(),
74                "cxxbridge: no #[cxx::bridge] module found in {}",
75                path.display(),
76            );
77        }
78        _ => {
79            let _ = io::stderr().write_fmt(format_args!("cxxbridge: {0}\n", report(error)))writeln!(io::stderr(), "cxxbridge: {}", report(error));
80        }
81    }
82    process::exit(1);
83}
84
85pub(crate) fn report(error: impl StdError) -> impl Display {
86    struct Report<E>(E);
87
88    impl<E: StdError> Display for Report<E> {
89        fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
90            formatter.write_fmt(format_args!("{0}", self.0))write!(formatter, "{}", self.0)?;
91            let mut error: &dyn StdError = &self.0;
92
93            while let Some(cause) = error.source() {
94                formatter.write_fmt(format_args!("\n\nCaused by:\n    {0}", cause))write!(formatter, "\n\nCaused by:\n    {}", cause)?;
95                error = cause;
96            }
97
98            Ok(())
99        }
100    }
101
102    Report(error)
103}
104
105fn sort_syn_errors(error: syn::Error) -> Vec<syn::Error> {
106    let mut errors: Vec<_> = error.into_iter().collect();
107    errors.sort_by_key(|e| {
108        let start = e.span().start();
109        (start.line, start.column)
110    });
111    errors
112}
113
114fn display_syn_error(
115    mut stderr: &mut dyn WriteColor,
116    path: &Path,
117    source: &str,
118    error: syn::Error,
119) {
120    let span = error.span();
121    let start = span.start();
122    let end = span.end();
123
124    let mut start_offset = 0;
125    for _ in 1..start.line {
126        start_offset += source[start_offset..].find('\n').unwrap() + 1;
127    }
128    let start_column = source[start_offset..]
129        .chars()
130        .take(start.column)
131        .map(char::len_utf8)
132        .sum::<usize>();
133    start_offset += start_column;
134
135    let mut end_offset = start_offset;
136    if start.line == end.line {
137        end_offset -= start_column;
138    } else {
139        for _ in 0..end.line - start.line {
140            end_offset += source[end_offset..].find('\n').unwrap() + 1;
141        }
142    }
143    end_offset += source[end_offset..]
144        .chars()
145        .take(end.column)
146        .map(char::len_utf8)
147        .sum::<usize>();
148
149    let mut path = path.to_string_lossy();
150    if path == "-" {
151        path = Cow::Borrowed(if truecfg!(unix) { "/dev/stdin" } else { "stdin" });
152    }
153
154    let mut files = SimpleFiles::new();
155    let file = files.add(path, source);
156
157    let diagnostic = diagnose(file, start_offset..end_offset, error);
158
159    let config = Config::default();
160    let _ = term::emit_to_write_style(&mut stderr, &config, &files, &diagnostic);
161}
162
163fn diagnose(file: usize, range: Range<usize>, error: syn::Error) -> Diagnostic<usize> {
164    let message = error.to_string();
165    let info = syntax::error::ERRORS
166        .iter()
167        .find(|e| message.contains(e.msg));
168    let mut diagnostic = Diagnostic::error().with_message(&message);
169    let mut label = Label::primary(file, range);
170    if let Some(info) = info {
171        label.message = info.label.map_or(message, str::to_owned);
172        diagnostic.labels.push(label);
173        diagnostic.notes.extend(info.note.map(str::to_owned));
174    } else {
175        label.message = message;
176        diagnostic.labels.push(label);
177    }
178    diagnostic.code = Some("cxxbridge".to_owned());
179    diagnostic
180}