wasm_bindgen_backend/
error.rs

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