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