napi_derive_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
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  /// Generate a `Diagnostic` from an informational message with no Span
43  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  /// Generate a `Diagnostic` from a Span and an informational message
53  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  /// Generate a `Diagnostic` from the span of any tokenizable object and a message
63  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  /// Attempt to generate a `Diagnostic` from a vector of other `Diagnostic` instances.
73  /// If the `Vec` is empty, returns `Ok(())`, otherwise returns the new `Diagnostic`
74  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  /// Immediately trigger a panic from this `Diagnostic`
85  #[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}