Skip to main content

wasm_bindgen_macro_support/
error.rs

1use proc_macro2::*;
2use quote::{ToTokens, TokenStreamExt};
3use syn::parse::Error;
4
5/// Provide a Diagnostic with the given span and message
6macro_rules! err_span {
7    ($span:expr, $($msg:tt)*) => (
8        $crate::Diagnostic::spanned_error(&$span, format!($($msg)*))
9    )
10}
11
12/// Immediately fail and return an Err, with the arguments passed to err_span!
13macro_rules! bail_span {
14    ($($t:tt)*) => (
15        return Err(err_span!($($t)*).into())
16    )
17}
18
19/// A struct representing a diagnostic to emit to the end-user as an error.
20#[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    /// Generate a `Diagnostic` from an informational message with no Span
39    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    /// Generate a `Diagnostic` from a Span and an informational message
49    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    /// Generate a `Diagnostic` from the span of any tokenizable object and a message
59    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    /// Attempt to generate a `Diagnostic` from a vector of other `Diagnostic` instances.
69    /// If the `Vec` is empty, returns `Ok(())`, otherwise returns the new `Diagnostic`
70    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    /// Immediately trigger a panic from this `Diagnostic`
81    #[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}