napi_derive_backend/
error.rs1use proc_macro2::*;
2use quote::{ToTokens, TokenStreamExt};
3use syn::parse::Error;
4
5#[macro_export]
7macro_rules! err_span {
8 ($span:expr, $($msg:tt)*) => (
9 $crate::Diagnostic::spanned_error(&$span, format!($($msg)*))
10 )
11}
12
13#[macro_export]
15macro_rules! bail_span {
16 ($($t:tt)*) => (
17 return Err(err_span!($($t)*).into())
18 )
19}
20
21#[derive(Debug)]
23pub struct Diagnostic {
24 inner: Repr,
25}
26
27pub type BindgenResult<T> = Result<T, Diagnostic>;
28
29#[derive(Debug)]
30enum Repr {
31 Single {
32 text: String,
33 span: Option<(Span, Span)>,
34 },
35 SynError(Error),
36 Multi {
37 diagnostics: Vec<Diagnostic>,
38 },
39}
40
41impl Diagnostic {
42 pub fn error<T: Into<String>>(text: T) -> Diagnostic {
44 Diagnostic {
45 inner: Repr::Single {
46 text: text.into(),
47 span: None,
48 },
49 }
50 }
51
52 pub fn span_error<T: Into<String>>(span: Span, text: T) -> Diagnostic {
54 Diagnostic {
55 inner: Repr::Single {
56 text: text.into(),
57 span: Some((span, span)),
58 },
59 }
60 }
61
62 pub fn spanned_error<T: Into<String>>(node: &dyn ToTokens, text: T) -> Diagnostic {
64 Diagnostic {
65 inner: Repr::Single {
66 text: text.into(),
67 span: extract_spans(node),
68 },
69 }
70 }
71
72 pub fn from_vec(diagnostics: Vec<Diagnostic>) -> BindgenResult<()> {
75 if diagnostics.is_empty() {
76 Ok(())
77 } else {
78 Err(Diagnostic {
79 inner: Repr::Multi { diagnostics },
80 })
81 }
82 }
83
84 #[allow(unconditional_recursion)]
86 pub fn panic(&self) -> ! {
87 match &self.inner {
88 Repr::Single { text, .. } => panic!("{}", text),
89 Repr::SynError(error) => panic!("{}", error),
90 Repr::Multi { diagnostics } => diagnostics[0].panic(),
91 }
92 }
93}
94
95impl From<Error> for Diagnostic {
96 fn from(err: Error) -> Diagnostic {
97 Diagnostic {
98 inner: Repr::SynError(err),
99 }
100 }
101}
102
103fn extract_spans(node: &dyn ToTokens) -> Option<(Span, Span)> {
104 let mut t = TokenStream::new();
105 node.to_tokens(&mut t);
106 let mut tokens = t.into_iter();
107 let start = tokens.next().map(|t| t.span());
108 let end = tokens.last().map(|t| t.span());
109 start.map(|start| (start, end.unwrap_or(start)))
110}
111
112impl ToTokens for Diagnostic {
113 fn to_tokens(&self, dst: &mut TokenStream) {
114 match &self.inner {
115 Repr::Single { text, span } => {
116 let cs2 = (Span::call_site(), Span::call_site());
117 let (start, end) = span.unwrap_or(cs2);
118 dst.append(Ident::new("compile_error", start));
119 dst.append(Punct::new('!', Spacing::Alone));
120 let mut message = TokenStream::new();
121 message.append(Literal::string(text));
122 let mut group = Group::new(Delimiter::Brace, message);
123 group.set_span(end);
124 dst.append(group);
125 }
126 Repr::Multi { diagnostics } => {
127 for diagnostic in diagnostics {
128 diagnostic.to_tokens(dst);
129 }
130 }
131 Repr::SynError(err) => {
132 err.to_compile_error().to_tokens(dst);
133 }
134 }
135 }
136}